info_profile_badge.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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_badge.h"
  8. #include "data/data_emoji_statuses.h"
  9. #include "data/data_peer.h"
  10. #include "data/data_session.h"
  11. #include "data/data_user.h"
  12. #include "data/stickers/data_custom_emoji.h"
  13. #include "info/profile/info_profile_values.h"
  14. #include "info/profile/info_profile_emoji_status_panel.h"
  15. #include "lang/lang_keys.h"
  16. #include "ui/widgets/buttons.h"
  17. #include "ui/painter.h"
  18. #include "ui/power_saving.h"
  19. #include "main/main_session.h"
  20. #include "styles/style_info.h"
  21. namespace Info::Profile {
  22. namespace {
  23. [[nodiscard]] bool HasPremiumClick(const Badge::Content &content) {
  24. return content.badge == BadgeType::Premium
  25. || (content.badge == BadgeType::Verified && content.emojiStatusId);
  26. }
  27. } // namespace
  28. Badge::Badge(
  29. not_null<QWidget*> parent,
  30. const style::InfoPeerBadge &st,
  31. not_null<Main::Session*> session,
  32. rpl::producer<Content> content,
  33. EmojiStatusPanel *emojiStatusPanel,
  34. Fn<bool()> animationPaused,
  35. int customStatusLoopsLimit,
  36. base::flags<BadgeType> allowed)
  37. : _parent(parent)
  38. , _st(st)
  39. , _session(session)
  40. , _emojiStatusPanel(emojiStatusPanel)
  41. , _customStatusLoopsLimit(customStatusLoopsLimit)
  42. , _allowed(allowed)
  43. , _animationPaused(std::move(animationPaused)) {
  44. std::move(
  45. content
  46. ) | rpl::start_with_next([=](Content content) {
  47. setContent(content);
  48. }, _lifetime);
  49. }
  50. Badge::~Badge() = default;
  51. Ui::RpWidget *Badge::widget() const {
  52. return _view.data();
  53. }
  54. void Badge::setContent(Content content) {
  55. if (!(_allowed & content.badge)
  56. || (!_session->premiumBadgesShown()
  57. && content.badge == BadgeType::Premium)) {
  58. content.badge = BadgeType::None;
  59. }
  60. if (!(_allowed & content.badge)) {
  61. content.badge = BadgeType::None;
  62. }
  63. if (_content == content) {
  64. return;
  65. }
  66. _content = content;
  67. _emojiStatus = nullptr;
  68. _view.destroy();
  69. if (_content.badge == BadgeType::None) {
  70. _updated.fire({});
  71. return;
  72. }
  73. _view.create(_parent);
  74. _view->show();
  75. switch (_content.badge) {
  76. case BadgeType::Verified:
  77. case BadgeType::BotVerified:
  78. case BadgeType::Premium: {
  79. const auto id = _content.emojiStatusId;
  80. const auto emoji = id
  81. ? (Data::FrameSizeFromTag(sizeTag())
  82. / style::DevicePixelRatio())
  83. : 0;
  84. const auto icon = (_content.badge == BadgeType::Verified)
  85. ? &_st.verified
  86. : id
  87. ? nullptr
  88. : &_st.premium;
  89. if (id) {
  90. _emojiStatus = _session->data().customEmojiManager().create(
  91. Data::EmojiStatusCustomId(id),
  92. [raw = _view.data()] { raw->update(); },
  93. sizeTag());
  94. if (_customStatusLoopsLimit > 0) {
  95. _emojiStatus = std::make_unique<Ui::Text::LimitedLoopsEmoji>(
  96. std::move(_emojiStatus),
  97. _customStatusLoopsLimit);
  98. }
  99. }
  100. const auto width = emoji + (icon ? icon->width() : 0);
  101. const auto height = std::max(emoji, icon ? icon->height() : 0);
  102. _view->resize(width, height);
  103. _view->paintRequest(
  104. ) | rpl::start_with_next([=, check = _view.data()]{
  105. if (_emojiStatus) {
  106. auto args = Ui::Text::CustomEmoji::Context{
  107. .textColor = _st.premiumFg->c,
  108. .now = crl::now(),
  109. .paused = ((_animationPaused && _animationPaused())
  110. || On(PowerSaving::kEmojiStatus)),
  111. };
  112. if (!_emojiStatusPanel
  113. || !_emojiStatusPanel->paintBadgeFrame(check)) {
  114. Painter p(check);
  115. _emojiStatus->paint(p, args);
  116. }
  117. }
  118. if (icon) {
  119. Painter p(check);
  120. icon->paint(p, emoji, 0, check->width());
  121. }
  122. }, _view->lifetime());
  123. } break;
  124. case BadgeType::Scam:
  125. case BadgeType::Fake: {
  126. const auto fake = (_content.badge == BadgeType::Fake);
  127. const auto size = Ui::ScamBadgeSize(fake);
  128. const auto skip = st::infoVerifiedCheckPosition.x();
  129. _view->resize(
  130. size.width() + 2 * skip,
  131. size.height() + 2 * skip);
  132. _view->paintRequest(
  133. ) | rpl::start_with_next([=, badge = _view.data()]{
  134. Painter p(badge);
  135. Ui::DrawScamBadge(
  136. fake,
  137. p,
  138. badge->rect().marginsRemoved({ skip, skip, skip, skip }),
  139. badge->width(),
  140. st::attentionButtonFg);
  141. }, _view->lifetime());
  142. } break;
  143. }
  144. if (!HasPremiumClick(_content) || !_premiumClickCallback) {
  145. _view->setAttribute(Qt::WA_TransparentForMouseEvents);
  146. } else {
  147. _view->setClickedCallback(_premiumClickCallback);
  148. }
  149. _updated.fire({});
  150. }
  151. void Badge::setPremiumClickCallback(Fn<void()> callback) {
  152. _premiumClickCallback = std::move(callback);
  153. if (_view && HasPremiumClick(_content)) {
  154. if (!_premiumClickCallback) {
  155. _view->setAttribute(Qt::WA_TransparentForMouseEvents);
  156. } else {
  157. _view->setAttribute(Qt::WA_TransparentForMouseEvents, false);
  158. _view->setClickedCallback(_premiumClickCallback);
  159. }
  160. }
  161. }
  162. rpl::producer<> Badge::updated() const {
  163. return _updated.events();
  164. }
  165. void Badge::move(int left, int top, int bottom) {
  166. if (!_view) {
  167. return;
  168. }
  169. const auto star = !_emojiStatus
  170. && (_content.badge == BadgeType::Premium
  171. || _content.badge == BadgeType::Verified);
  172. const auto fake = !_emojiStatus && !star;
  173. const auto skip = fake ? 0 : _st.position.x();
  174. const auto badgeLeft = left + skip;
  175. const auto badgeTop = top
  176. + (star
  177. ? _st.position.y()
  178. : (bottom - top - _view->height()) / 2);
  179. _view->moveToLeft(badgeLeft, badgeTop);
  180. }
  181. Data::CustomEmojiSizeTag Badge::sizeTag() const {
  182. using SizeTag = Data::CustomEmojiSizeTag;
  183. return (_st.sizeTag == 2)
  184. ? SizeTag::Isolated
  185. : (_st.sizeTag == 1)
  186. ? SizeTag::Large
  187. : SizeTag::Normal;
  188. }
  189. rpl::producer<Badge::Content> BadgeContentForPeer(not_null<PeerData*> peer) {
  190. const auto statusOnlyForPremium = peer->isUser();
  191. return rpl::combine(
  192. BadgeValue(peer),
  193. EmojiStatusIdValue(peer)
  194. ) | rpl::map([=](BadgeType badge, EmojiStatusId emojiStatusId) {
  195. if (badge == BadgeType::Verified) {
  196. badge = BadgeType::None;
  197. }
  198. if (statusOnlyForPremium && badge != BadgeType::Premium) {
  199. emojiStatusId = EmojiStatusId();
  200. } else if (emojiStatusId && badge == BadgeType::None) {
  201. badge = BadgeType::Premium;
  202. }
  203. return Badge::Content{ badge, emojiStatusId };
  204. });
  205. }
  206. rpl::producer<Badge::Content> VerifiedContentForPeer(
  207. not_null<PeerData*> peer) {
  208. return BadgeValue(peer) | rpl::map([=](BadgeType badge) {
  209. if (badge != BadgeType::Verified) {
  210. badge = BadgeType::None;
  211. }
  212. return Badge::Content{ badge };
  213. });
  214. }
  215. } // namespace Info::Profile