info_profile_values.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  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 "info/profile/info_profile_values.h"
  8. #include "api/api_chat_participants.h"
  9. #include "apiwrap.h"
  10. #include "info/profile/info_profile_phone_menu.h"
  11. #include "info/profile/info_profile_badge.h"
  12. #include "core/application.h"
  13. #include "core/click_handler_types.h"
  14. #include "countries/countries_instance.h"
  15. #include "main/main_session.h"
  16. #include "ui/wrap/slide_wrap.h"
  17. #include "ui/text/format_values.h" // Ui::FormatPhone
  18. #include "ui/text/text_utilities.h"
  19. #include "lang/lang_keys.h"
  20. #include "data/notify/data_notify_settings.h"
  21. #include "data/data_peer_values.h"
  22. #include "data/data_saved_messages.h"
  23. #include "data/data_saved_sublist.h"
  24. #include "data/data_shared_media.h"
  25. #include "data/data_message_reactions.h"
  26. #include "data/data_folder.h"
  27. #include "data/data_changes.h"
  28. #include "data/data_channel.h"
  29. #include "data/data_chat.h"
  30. #include "data/data_user.h"
  31. #include "data/data_forum_topic.h"
  32. #include "data/data_session.h"
  33. #include "data/data_premium_limits.h"
  34. #include "boxes/peers/edit_peer_permissions_box.h"
  35. #include "base/unixtime.h"
  36. namespace Info {
  37. namespace Profile {
  38. namespace {
  39. using UpdateFlag = Data::PeerUpdate::Flag;
  40. auto PlainAboutValue(not_null<PeerData*> peer) {
  41. return peer->session().changes().peerFlagsValue(
  42. peer,
  43. UpdateFlag::About
  44. ) | rpl::map([=] {
  45. return peer->about();
  46. });
  47. }
  48. auto PlainUsernameValue(not_null<PeerData*> peer) {
  49. return rpl::merge(
  50. peer->session().changes().peerFlagsValue(peer, UpdateFlag::Username),
  51. peer->session().changes().peerFlagsValue(peer, UpdateFlag::Usernames)
  52. ) | rpl::map([=] {
  53. return peer->username();
  54. });
  55. }
  56. auto PlainPrimaryUsernameValue(not_null<PeerData*> peer) {
  57. return UsernamesValue(
  58. peer
  59. ) | rpl::map([=](std::vector<TextWithEntities> usernames) {
  60. if (!usernames.empty()) {
  61. return rpl::single(usernames.front().text) | rpl::type_erased();
  62. } else {
  63. return PlainUsernameValue(peer) | rpl::type_erased();
  64. }
  65. }) | rpl::flatten_latest();
  66. }
  67. void StripExternalLinks(TextWithEntities &text) {
  68. const auto local = [](const QString &url) {
  69. return !UrlRequiresConfirmation(QUrl::fromUserInput(url));
  70. };
  71. const auto notLocal = [&](const EntityInText &entity) {
  72. if (entity.type() == EntityType::CustomUrl) {
  73. return !local(entity.data());
  74. } else if (entity.type() == EntityType::Url) {
  75. return !local(text.text.mid(entity.offset(), entity.length()));
  76. } else {
  77. return false;
  78. }
  79. };
  80. text.entities.erase(
  81. ranges::remove_if(text.entities, notLocal),
  82. text.entities.end());
  83. }
  84. } // namespace
  85. rpl::producer<QString> NameValue(not_null<PeerData*> peer) {
  86. return peer->session().changes().peerFlagsValue(
  87. peer,
  88. UpdateFlag::Name
  89. ) | rpl::map([=] { return peer->name(); });
  90. }
  91. rpl::producer<QString> TitleValue(not_null<Data::ForumTopic*> topic) {
  92. return topic->session().changes().topicFlagsValue(
  93. topic,
  94. Data::TopicUpdate::Flag::Title
  95. ) | rpl::map([=] { return topic->title(); });
  96. }
  97. rpl::producer<DocumentId> IconIdValue(not_null<Data::ForumTopic*> topic) {
  98. return topic->session().changes().topicFlagsValue(
  99. topic,
  100. Data::TopicUpdate::Flag::IconId
  101. ) | rpl::map([=] { return topic->iconId(); });
  102. }
  103. rpl::producer<int32> ColorIdValue(not_null<Data::ForumTopic*> topic) {
  104. return topic->session().changes().topicFlagsValue(
  105. topic,
  106. Data::TopicUpdate::Flag::ColorId
  107. ) | rpl::map([=] { return topic->colorId(); });
  108. }
  109. rpl::producer<TextWithEntities> PhoneValue(not_null<UserData*> user) {
  110. return rpl::merge(
  111. Countries::Instance().updated(),
  112. user->session().changes().peerFlagsValue(
  113. user,
  114. UpdateFlag::PhoneNumber) | rpl::to_empty
  115. ) | rpl::map([=] {
  116. return Ui::FormatPhone(user->phone());
  117. }) | Ui::Text::ToWithEntities();
  118. }
  119. rpl::producer<TextWithEntities> PhoneOrHiddenValue(not_null<UserData*> user) {
  120. return rpl::combine(
  121. PhoneValue(user),
  122. PlainUsernameValue(user),
  123. PlainAboutValue(user),
  124. tr::lng_info_mobile_hidden()
  125. ) | rpl::map([user](
  126. const TextWithEntities &phone,
  127. const QString &username,
  128. const QString &about,
  129. const QString &hidden) {
  130. if (phone.text.isEmpty() && username.isEmpty() && about.isEmpty()) {
  131. return Ui::Text::WithEntities(hidden);
  132. } else if (IsCollectiblePhone(user)) {
  133. return Ui::Text::Link(phone, u"internal:collectible_phone/"_q
  134. + user->phone() + '@' + QString::number(user->id.value));
  135. } else {
  136. return phone;
  137. }
  138. });
  139. }
  140. rpl::producer<TextWithEntities> UsernameValue(
  141. not_null<PeerData*> peer,
  142. bool primary) {
  143. return (primary
  144. ? PlainPrimaryUsernameValue(peer)
  145. : (PlainUsernameValue(peer) | rpl::type_erased())
  146. ) | rpl::map([](QString &&username) {
  147. return username.isEmpty()
  148. ? QString()
  149. : ('@' + username);
  150. }) | Ui::Text::ToWithEntities();
  151. }
  152. QString UsernameUrl(
  153. not_null<PeerData*> peer,
  154. const QString &username,
  155. bool link) {
  156. const auto type = !peer->isUsernameEditable(username)
  157. ? u"collectible_username"_q
  158. : link
  159. ? u"username_link"_q
  160. : u"username_regular"_q;
  161. return u"internal:"_q
  162. + type
  163. + u"/"_q
  164. + username
  165. + "@"
  166. + QString::number(peer->id.value);
  167. }
  168. rpl::producer<std::vector<TextWithEntities>> UsernamesValue(
  169. not_null<PeerData*> peer) {
  170. const auto map = [=](const std::vector<QString> &usernames) {
  171. return ranges::views::all(
  172. usernames
  173. ) | ranges::views::transform([&](const QString &u) {
  174. return Ui::Text::Link(u, UsernameUrl(peer, u));
  175. }) | ranges::to_vector;
  176. };
  177. auto value = rpl::merge(
  178. peer->session().changes().peerFlagsValue(peer, UpdateFlag::Username),
  179. peer->session().changes().peerFlagsValue(peer, UpdateFlag::Usernames)
  180. );
  181. if (const auto user = peer->asUser()) {
  182. return std::move(value) | rpl::map([=] {
  183. return map(user->usernames());
  184. });
  185. } else if (const auto channel = peer->asChannel()) {
  186. return std::move(value) | rpl::map([=] {
  187. return map(channel->usernames());
  188. });
  189. } else {
  190. return rpl::single(std::vector<TextWithEntities>());
  191. }
  192. }
  193. TextWithEntities AboutWithEntities(
  194. not_null<PeerData*> peer,
  195. const QString &value) {
  196. auto flags = TextParseLinks | TextParseMentions;
  197. const auto user = peer->asUser();
  198. const auto isBot = user && user->isBot();
  199. const auto isPremium = user && user->isPremium();
  200. if (!user) {
  201. flags |= TextParseHashtags;
  202. } else if (isBot) {
  203. flags |= TextParseHashtags | TextParseBotCommands;
  204. }
  205. const auto stripExternal = peer->isChat()
  206. || peer->isMegagroup()
  207. || (user && !isBot && !isPremium);
  208. auto result = TextWithEntities{ value };
  209. TextUtilities::ParseEntities(result, flags);
  210. if (stripExternal) {
  211. StripExternalLinks(result);
  212. }
  213. return result;
  214. }
  215. rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer) {
  216. return PlainAboutValue(
  217. peer
  218. ) | rpl::map([peer](const QString &value) {
  219. return AboutWithEntities(peer, value);
  220. });
  221. }
  222. rpl::producer<LinkWithUrl> LinkValue(not_null<PeerData*> peer, bool primary) {
  223. return (primary
  224. ? PlainPrimaryUsernameValue(peer)
  225. : PlainUsernameValue(peer) | rpl::type_erased()
  226. ) | rpl::map([=](QString &&username) {
  227. return LinkWithUrl{
  228. .text = (username.isEmpty()
  229. ? QString()
  230. : peer->session().createInternalLinkFull(username)),
  231. .url = (username.isEmpty()
  232. ? QString()
  233. : UsernameUrl(peer, username, true)),
  234. };
  235. });
  236. }
  237. rpl::producer<const ChannelLocation*> LocationValue(
  238. not_null<ChannelData*> channel) {
  239. return channel->session().changes().peerFlagsValue(
  240. channel,
  241. UpdateFlag::ChannelLocation
  242. ) | rpl::map([=] {
  243. return channel->getLocation();
  244. });
  245. }
  246. rpl::producer<bool> NotificationsEnabledValue(
  247. not_null<Data::Thread*> thread) {
  248. const auto topic = thread->asTopic();
  249. if (!topic) {
  250. return NotificationsEnabledValue(thread->peer());
  251. }
  252. return rpl::merge(
  253. topic->session().changes().topicFlagsValue(
  254. topic,
  255. Data::TopicUpdate::Flag::Notifications
  256. ) | rpl::to_empty,
  257. topic->session().changes().peerUpdates(
  258. topic->channel(),
  259. UpdateFlag::Notifications
  260. ) | rpl::to_empty,
  261. topic->owner().notifySettings().defaultUpdates(topic->channel())
  262. ) | rpl::map([=] {
  263. return !topic->owner().notifySettings().isMuted(topic);
  264. }) | rpl::distinct_until_changed();
  265. }
  266. rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer) {
  267. return rpl::merge(
  268. peer->session().changes().peerFlagsValue(
  269. peer,
  270. UpdateFlag::Notifications
  271. ) | rpl::to_empty,
  272. peer->owner().notifySettings().defaultUpdates(peer)
  273. ) | rpl::map([=] {
  274. return !peer->owner().notifySettings().isMuted(peer);
  275. }) | rpl::distinct_until_changed();
  276. }
  277. rpl::producer<bool> IsContactValue(not_null<UserData*> user) {
  278. return user->session().changes().peerFlagsValue(
  279. user,
  280. UpdateFlag::IsContact
  281. ) | rpl::map([=] {
  282. return user->isContact();
  283. });
  284. }
  285. [[nodiscard]] rpl::producer<QString> InviteToChatButton(
  286. not_null<UserData*> user) {
  287. if (!user->isBot()
  288. || user->isRepliesChat()
  289. || user->isVerifyCodes()
  290. || user->isSupport()) {
  291. return rpl::single(QString());
  292. }
  293. using Flag = Data::PeerUpdate::Flag;
  294. return user->session().changes().peerFlagsValue(
  295. user,
  296. Flag::BotCanBeInvited | Flag::Rights
  297. ) | rpl::map([=] {
  298. const auto info = user->botInfo.get();
  299. return info->cantJoinGroups
  300. ? (info->channelAdminRights
  301. ? tr::lng_profile_invite_to_channel(tr::now)
  302. : QString())
  303. : (info->channelAdminRights
  304. ? tr::lng_profile_add_bot_as_admin(tr::now)
  305. : tr::lng_profile_invite_to_group(tr::now));
  306. });
  307. }
  308. [[nodiscard]] rpl::producer<QString> InviteToChatAbout(
  309. not_null<UserData*> user) {
  310. if (!user->isBot()
  311. || user->isRepliesChat()
  312. || user->isVerifyCodes()
  313. || user->isSupport()) {
  314. return rpl::single(QString());
  315. }
  316. using Flag = Data::PeerUpdate::Flag;
  317. return user->session().changes().peerFlagsValue(
  318. user,
  319. Flag::BotCanBeInvited | Flag::Rights
  320. ) | rpl::map([=] {
  321. const auto info = user->botInfo.get();
  322. return (info->cantJoinGroups || !info->groupAdminRights)
  323. ? (info->channelAdminRights
  324. ? tr::lng_profile_invite_to_channel_about(tr::now)
  325. : QString())
  326. : (info->channelAdminRights
  327. ? tr::lng_profile_add_bot_as_admin_about(tr::now)
  328. : tr::lng_profile_invite_to_group_about(tr::now));
  329. });
  330. }
  331. rpl::producer<bool> CanShareContactValue(not_null<UserData*> user) {
  332. return user->session().changes().peerFlagsValue(
  333. user,
  334. UpdateFlag::CanShareContact
  335. ) | rpl::map([=] {
  336. return user->canShareThisContact();
  337. });
  338. }
  339. rpl::producer<bool> CanAddContactValue(not_null<UserData*> user) {
  340. using namespace rpl::mappers;
  341. if (user->isBot() || user->isSelf() || user->isInaccessible()) {
  342. return rpl::single(false);
  343. }
  344. return IsContactValue(
  345. user
  346. ) | rpl::map(!_1);
  347. }
  348. rpl::producer<Data::Birthday> BirthdayValue(not_null<UserData*> user) {
  349. return user->session().changes().peerFlagsValue(
  350. user,
  351. UpdateFlag::Birthday
  352. ) | rpl::map([=] {
  353. return user->birthday();
  354. });
  355. }
  356. rpl::producer<ChannelData*> PersonalChannelValue(not_null<UserData*> user) {
  357. return user->session().changes().peerFlagsValue(
  358. user,
  359. UpdateFlag::PersonalChannel
  360. ) | rpl::map([=] {
  361. const auto channelId = user->personalChannelId();
  362. return channelId ? user->owner().channel(channelId).get() : nullptr;
  363. });
  364. }
  365. rpl::producer<bool> AmInChannelValue(not_null<ChannelData*> channel) {
  366. return channel->session().changes().peerFlagsValue(
  367. channel,
  368. UpdateFlag::ChannelAmIn
  369. ) | rpl::map([=] {
  370. return channel->amIn();
  371. });
  372. }
  373. rpl::producer<int> MembersCountValue(not_null<PeerData*> peer) {
  374. if (const auto chat = peer->asChat()) {
  375. return peer->session().changes().peerFlagsValue(
  376. peer,
  377. UpdateFlag::Members
  378. ) | rpl::map([=] {
  379. return chat->amIn()
  380. ? std::max(chat->count, int(chat->participants.size()))
  381. : 0;
  382. });
  383. } else if (const auto channel = peer->asChannel()) {
  384. return peer->session().changes().peerFlagsValue(
  385. peer,
  386. UpdateFlag::Members
  387. ) | rpl::map([=] {
  388. return channel->membersCount();
  389. });
  390. }
  391. Unexpected("User in MembersCountViewer().");
  392. }
  393. rpl::producer<int> PendingRequestsCountValue(not_null<PeerData*> peer) {
  394. if (const auto chat = peer->asChat()) {
  395. return peer->session().changes().peerFlagsValue(
  396. peer,
  397. UpdateFlag::PendingRequests
  398. ) | rpl::map([=] {
  399. return chat->pendingRequestsCount();
  400. });
  401. } else if (const auto channel = peer->asChannel()) {
  402. return peer->session().changes().peerFlagsValue(
  403. peer,
  404. UpdateFlag::PendingRequests
  405. ) | rpl::map([=] {
  406. return channel->pendingRequestsCount();
  407. });
  408. }
  409. Unexpected("User in MembersCountViewer().");
  410. }
  411. rpl::producer<int> AdminsCountValue(not_null<PeerData*> peer) {
  412. if (const auto chat = peer->asChat()) {
  413. return peer->session().changes().peerFlagsValue(
  414. peer,
  415. UpdateFlag::Admins | UpdateFlag::Rights
  416. ) | rpl::map([=] {
  417. return chat->participants.empty()
  418. ? 0
  419. : int(chat->admins.size() + (chat->creator ? 1 : 0));
  420. });
  421. } else if (const auto channel = peer->asChannel()) {
  422. return peer->session().changes().peerFlagsValue(
  423. peer,
  424. UpdateFlag::Admins | UpdateFlag::Rights
  425. ) | rpl::map([=] {
  426. return channel->canViewAdmins()
  427. ? channel->adminsCount()
  428. : 0;
  429. });
  430. }
  431. Unexpected("User in AdminsCountValue().");
  432. }
  433. rpl::producer<int> RestrictionsCountValue(not_null<PeerData*> peer) {
  434. const auto countOfRestrictions = [](
  435. Data::RestrictionsSetOptions options,
  436. ChatRestrictions restrictions) {
  437. auto count = 0;
  438. const auto list = Data::ListOfRestrictions(options);
  439. for (const auto &f : list) {
  440. if (restrictions & f) count++;
  441. }
  442. return int(list.size()) - count;
  443. };
  444. if (const auto chat = peer->asChat()) {
  445. return peer->session().changes().peerFlagsValue(
  446. peer,
  447. UpdateFlag::Rights
  448. ) | rpl::map([=] {
  449. return countOfRestrictions({}, chat->defaultRestrictions());
  450. });
  451. } else if (const auto channel = peer->asChannel()) {
  452. return rpl::combine(
  453. Data::PeerFlagValue(channel, ChannelData::Flag::Forum),
  454. channel->session().changes().peerFlagsValue(
  455. channel,
  456. UpdateFlag::Rights)
  457. ) | rpl::map([=] {
  458. return countOfRestrictions(
  459. { .isForum = channel->isForum() },
  460. channel->defaultRestrictions());
  461. });
  462. }
  463. Unexpected("User in RestrictionsCountValue().");
  464. }
  465. rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
  466. not_null<PeerData*> peer) {
  467. if (const auto chat = peer->asChat()) {
  468. return peer->session().changes().peerFlagsValue(
  469. peer,
  470. UpdateFlag::Migration
  471. ) | rpl::map([=] {
  472. return chat->migrateToOrMe();
  473. });
  474. } else {
  475. return rpl::single(peer);
  476. }
  477. }
  478. rpl::producer<int> RestrictedCountValue(not_null<ChannelData*> channel) {
  479. return channel->session().changes().peerFlagsValue(
  480. channel,
  481. UpdateFlag::BannedUsers | UpdateFlag::Rights
  482. ) | rpl::map([=] {
  483. return channel->canViewBanned()
  484. ? channel->restrictedCount()
  485. : 0;
  486. });
  487. }
  488. rpl::producer<int> KickedCountValue(not_null<ChannelData*> channel) {
  489. return channel->session().changes().peerFlagsValue(
  490. channel,
  491. UpdateFlag::BannedUsers | UpdateFlag::Rights
  492. ) | rpl::map([=] {
  493. return channel->canViewBanned()
  494. ? channel->kickedCount()
  495. : 0;
  496. });
  497. }
  498. rpl::producer<int> SharedMediaCountValue(
  499. not_null<PeerData*> peer,
  500. MsgId topicRootId,
  501. PeerData *migrated,
  502. Storage::SharedMediaType type) {
  503. auto aroundId = 0;
  504. auto limit = 0;
  505. auto updated = SharedMediaMergedViewer(
  506. &peer->session(),
  507. SharedMediaMergedKey(
  508. SparseIdsMergedSlice::Key(
  509. peer->id,
  510. topicRootId,
  511. migrated ? migrated->id : 0,
  512. aroundId),
  513. type),
  514. limit,
  515. limit
  516. ) | rpl::map([](const SparseIdsMergedSlice &slice) {
  517. return slice.fullCount();
  518. }) | rpl::filter_optional();
  519. return rpl::single(0) | rpl::then(std::move(updated));
  520. }
  521. rpl::producer<int> CommonGroupsCountValue(not_null<UserData*> user) {
  522. return user->session().changes().peerFlagsValue(
  523. user,
  524. UpdateFlag::CommonChats
  525. ) | rpl::map([=] {
  526. return user->commonChatsCount();
  527. });
  528. }
  529. rpl::producer<int> SimilarPeersCountValue(
  530. not_null<PeerData*> peer) {
  531. const auto participants = &peer->session().api().chatParticipants();
  532. participants->loadSimilarPeers(peer);
  533. return rpl::single(peer) | rpl::then(
  534. participants->similarLoaded()
  535. ) | rpl::filter(
  536. rpl::mappers::_1 == peer
  537. ) | rpl::map([=] {
  538. const auto &similar = participants->similar(peer);
  539. return int(similar.list.size()) + similar.more;
  540. });
  541. }
  542. rpl::producer<int> SavedSublistCountValue(
  543. not_null<PeerData*> peer) {
  544. const auto saved = &peer->owner().savedMessages();
  545. const auto sublist = saved->sublist(peer);
  546. if (!sublist->fullCount()) {
  547. saved->loadMore(sublist);
  548. return rpl::single(0) | rpl::then(sublist->fullCountValue());
  549. }
  550. return sublist->fullCountValue();
  551. }
  552. rpl::producer<int> PeerGiftsCountValue(not_null<PeerData*> peer) {
  553. return peer->session().changes().peerFlagsValue(
  554. peer,
  555. UpdateFlag::PeerGifts
  556. ) | rpl::map([=] {
  557. return peer->peerGiftsCount();
  558. });
  559. }
  560. rpl::producer<bool> CanAddMemberValue(not_null<PeerData*> peer) {
  561. if (const auto chat = peer->asChat()) {
  562. return peer->session().changes().peerFlagsValue(
  563. peer,
  564. UpdateFlag::Rights
  565. ) | rpl::map([=] {
  566. return chat->canAddMembers();
  567. });
  568. } else if (const auto channel = peer->asChannel()) {
  569. return peer->session().changes().peerFlagsValue(
  570. peer,
  571. UpdateFlag::Rights
  572. ) | rpl::map([=] {
  573. return channel->canAddMembers();
  574. });
  575. }
  576. return rpl::single(false);
  577. }
  578. rpl::producer<int> FullReactionsCountValue(
  579. not_null<Main::Session*> session) {
  580. const auto reactions = &session->data().reactions();
  581. return rpl::single(rpl::empty) | rpl::then(
  582. reactions->defaultUpdates()
  583. ) | rpl::map([=] {
  584. return int(reactions->list(Data::Reactions::Type::Active).size());
  585. }) | rpl::distinct_until_changed();
  586. }
  587. rpl::producer<bool> CanViewParticipantsValue(
  588. not_null<ChannelData*> megagroup) {
  589. if (megagroup->amCreator()) {
  590. return rpl::single(true);
  591. }
  592. return rpl::combine(
  593. megagroup->session().changes().peerFlagsValue(
  594. megagroup,
  595. UpdateFlag::Rights),
  596. megagroup->flagsValue(),
  597. [=] { return megagroup->canViewMembers(); }
  598. ) | rpl::distinct_until_changed();
  599. }
  600. template <typename Flag, typename Peer>
  601. rpl::producer<BadgeType> BadgeValueFromFlags(Peer peer) {
  602. return rpl::combine(
  603. Data::PeerFlagsValue(
  604. peer,
  605. Flag::Verified | Flag::Scam | Flag::Fake),
  606. Data::PeerPremiumValue(peer)
  607. ) | rpl::map([=](base::flags<Flag> value, bool premium) {
  608. return (value & Flag::Scam)
  609. ? BadgeType::Scam
  610. : (value & Flag::Fake)
  611. ? BadgeType::Fake
  612. : (value & Flag::Verified)
  613. ? BadgeType::Verified
  614. : premium
  615. ? BadgeType::Premium
  616. : BadgeType::None;
  617. });
  618. }
  619. rpl::producer<BadgeType> BadgeValue(not_null<PeerData*> peer) {
  620. if (const auto user = peer->asUser()) {
  621. return BadgeValueFromFlags<UserDataFlag>(user);
  622. } else if (const auto channel = peer->asChannel()) {
  623. return BadgeValueFromFlags<ChannelDataFlag>(channel);
  624. }
  625. return rpl::single(BadgeType::None);
  626. }
  627. rpl::producer<EmojiStatusId> EmojiStatusIdValue(not_null<PeerData*> peer) {
  628. if (peer->isChat()) {
  629. return rpl::single(EmojiStatusId());
  630. }
  631. return peer->session().changes().peerFlagsValue(
  632. peer,
  633. Data::PeerUpdate::Flag::EmojiStatus
  634. ) | rpl::map([=] { return peer->emojiStatusId(); });
  635. }
  636. rpl::producer<QString> BirthdayLabelText(
  637. rpl::producer<Data::Birthday> birthday) {
  638. return std::move(birthday) | rpl::map([](Data::Birthday value) {
  639. return rpl::conditional(
  640. Data::IsBirthdayTodayValue(value),
  641. tr::lng_info_birthday_today_label(),
  642. tr::lng_info_birthday_label());
  643. }) | rpl::flatten_latest();
  644. }
  645. rpl::producer<QString> BirthdayValueText(
  646. rpl::producer<Data::Birthday> birthday) {
  647. return std::move(
  648. birthday
  649. ) | rpl::map([](Data::Birthday value) -> rpl::producer<QString> {
  650. if (!value) {
  651. return rpl::single(QString());
  652. }
  653. return Data::IsBirthdayTodayValue(
  654. value
  655. ) | rpl::map([=](bool today) {
  656. auto text = Data::BirthdayText(value);
  657. if (const auto age = Data::BirthdayAge(value)) {
  658. text = (today
  659. ? tr::lng_info_birthday_today_years
  660. : tr::lng_info_birthday_years)(
  661. tr::now,
  662. lt_count,
  663. age,
  664. lt_date,
  665. text);
  666. }
  667. if (today) {
  668. text = tr::lng_info_birthday_today(
  669. tr::now,
  670. lt_emoji,
  671. Data::BirthdayCake(),
  672. lt_date,
  673. text);
  674. }
  675. return text;
  676. });
  677. }) | rpl::flatten_latest();
  678. }
  679. } // namespace Profile
  680. } // namespace Info