collectible_info_box.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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 "ui/boxes/collectible_info_box.h"
  8. #include "base/unixtime.h"
  9. #include "core/file_utilities.h"
  10. #include "lang/lang_keys.h"
  11. #include "lottie/lottie_icon.h"
  12. #include "info/channel_statistics/earn/earn_format.h"
  13. #include "ui/layers/generic_box.h"
  14. #include "ui/text/format_values.h"
  15. #include "ui/text/text_utilities.h"
  16. #include "ui/widgets/buttons.h"
  17. #include "ui/widgets/labels.h"
  18. #include "ui/dynamic_image.h"
  19. #include "ui/painter.h"
  20. #include "settings/settings_common.h"
  21. #include "styles/style_boxes.h"
  22. #include "styles/style_layers.h"
  23. #include <QtCore/QRegularExpression>
  24. #include <QtGui/QGuiApplication>
  25. namespace Ui {
  26. namespace {
  27. constexpr auto kTonMultiplier = uint64(1000000000);
  28. [[nodiscard]] QString FormatEntity(CollectibleType type, QString entity) {
  29. switch (type) {
  30. case CollectibleType::Phone: {
  31. static const auto kNonDigits = QRegularExpression(u"[^\\d]"_q);
  32. entity.replace(kNonDigits, QString());
  33. } return Ui::FormatPhone(entity);
  34. case CollectibleType::Username:
  35. return entity.startsWith('@') ? entity : ('@' + entity);
  36. }
  37. Unexpected("CollectibleType in FormatEntity.");
  38. }
  39. [[nodiscard]] QString FormatDate(TimeId date) {
  40. return langDateTime(base::unixtime::parse(date));
  41. }
  42. [[nodiscard]] TextWithEntities FormatPrice(
  43. const CollectibleInfo &info,
  44. const CollectibleDetails &details) {
  45. auto minor = Info::ChannelEarn::MinorPart(info.cryptoAmount);
  46. if (minor.size() == 1 && minor.at(0) == '.') {
  47. minor += '0';
  48. }
  49. auto price = (info.cryptoCurrency == u"TON"_q)
  50. ? base::duplicate(
  51. details.tonEmoji
  52. ).append(
  53. Info::ChannelEarn::MajorPart(info.cryptoAmount)
  54. ).append(minor)
  55. : TextWithEntities{ ('{'
  56. + info.cryptoCurrency + ':' + QString::number(info.cryptoAmount)
  57. + '}') };
  58. const auto fiat = Ui::FillAmountAndCurrency(info.amount, info.currency);
  59. return Ui::Text::Wrapped(
  60. price,
  61. EntityType::Bold
  62. ).append(u" ("_q + fiat + ')');
  63. }
  64. [[nodiscard]] object_ptr<Ui::RpWidget> MakeOwnerCell(
  65. not_null<QWidget*> parent,
  66. const CollectibleInfo &info) {
  67. const auto st = &st::defaultMultiSelectItem;
  68. const auto size = st->height;
  69. auto result = object_ptr<Ui::FixedHeightWidget>(parent.get(), size);
  70. const auto raw = result.data();
  71. const auto name = info.ownerName;
  72. const auto userpic = info.ownerUserpic;
  73. const auto nameWidth = st->style.font->width(name);
  74. const auto added = size + st->padding.left() + st->padding.right();
  75. const auto subscribed = std::make_shared<bool>(false);
  76. raw->paintRequest() | rpl::start_with_next([=] {
  77. const auto use = std::min(nameWidth + added, raw->width());
  78. const auto x = (raw->width() - use) / 2;
  79. if (const auto available = use - added; available > 0) {
  80. auto p = QPainter(raw);
  81. auto hq = PainterHighQualityEnabler(p);
  82. p.setPen(Qt::NoPen);
  83. p.setBrush(st->textBg);
  84. p.drawRoundedRect(x, 0, use, size, size / 2., size / 2.);
  85. if (!*subscribed) {
  86. *subscribed = true;
  87. userpic->subscribeToUpdates([=] { raw->update(); });
  88. }
  89. p.drawImage(QRect(x, 0, size, size), userpic->image(size));
  90. const auto textx = x + size + st->padding.left();
  91. const auto texty = st->padding.top() + st->style.font->ascent;
  92. const auto text = (use == nameWidth + added)
  93. ? name
  94. : st->style.font->elided(name, available);
  95. p.setPen(st->textFg);
  96. p.setFont(st->style.font);
  97. p.drawText(textx, texty, text);
  98. }
  99. }, raw->lifetime());
  100. return result;
  101. }
  102. } // namespace
  103. CollectibleType DetectCollectibleType(const QString &entity) {
  104. return entity.startsWith('+')
  105. ? CollectibleType::Phone
  106. : CollectibleType::Username;
  107. }
  108. void CollectibleInfoBox(
  109. not_null<Ui::GenericBox*> box,
  110. CollectibleInfo info,
  111. CollectibleDetails details) {
  112. box->setWidth(st::boxWideWidth);
  113. box->setStyle(st::collectibleBox);
  114. const auto type = DetectCollectibleType(info.entity);
  115. const auto icon = box->addRow(
  116. object_ptr<Ui::FixedHeightWidget>(box, st::collectibleIconDiameter),
  117. st::collectibleIconPadding);
  118. icon->paintRequest(
  119. ) | rpl::start_with_next([=](QRect clip) {
  120. const auto size = icon->height();
  121. const auto inner = QRect(
  122. (icon->width() - size) / 2,
  123. 0,
  124. size,
  125. size);
  126. if (!inner.intersects(clip)) {
  127. return;
  128. }
  129. auto p = QPainter(icon);
  130. auto hq = PainterHighQualityEnabler(p);
  131. p.setBrush(st::defaultActiveButton.textBg);
  132. p.setPen(Qt::NoPen);
  133. p.drawEllipse(inner);
  134. }, icon->lifetime());
  135. const auto lottieSize = st::collectibleIcon;
  136. auto lottie = Settings::CreateLottieIcon(
  137. icon,
  138. {
  139. .name = (type == CollectibleType::Phone
  140. ? u"collectible_phone"_q
  141. : u"collectible_username"_q),
  142. .color = &st::defaultActiveButton.textFg,
  143. .sizeOverride = { lottieSize, lottieSize },
  144. },
  145. QMargins());
  146. box->showFinishes(
  147. ) | rpl::start_with_next([animate = std::move(lottie.animate)] {
  148. animate(anim::repeat::once);
  149. }, box->lifetime());
  150. const auto animation = lottie.widget.release();
  151. icon->sizeValue() | rpl::start_with_next([=](QSize size) {
  152. const auto skip = (type == CollectibleType::Phone)
  153. ? style::ConvertScale(2)
  154. : 0;
  155. animation->move(
  156. (size.width() - animation->width()) / 2,
  157. skip + (size.height() - animation->height()) / 2);
  158. }, animation->lifetime());
  159. const auto formatted = FormatEntity(type, info.entity);
  160. const auto header = (type == CollectibleType::Phone)
  161. ? tr::lng_collectible_phone_title(
  162. tr::now,
  163. lt_phone,
  164. Ui::Text::Link(formatted),
  165. Ui::Text::WithEntities)
  166. : tr::lng_collectible_username_title(
  167. tr::now,
  168. lt_username,
  169. Ui::Text::Link(formatted),
  170. Ui::Text::WithEntities);
  171. const auto copyCallback = [box, type, formatted, text = info.copyText](
  172. bool copyLink) {
  173. QGuiApplication::clipboard()->setText((text.isEmpty() || !copyLink)
  174. ? formatted
  175. : text);
  176. box->uiShow()->showToast((type == CollectibleType::Phone)
  177. ? tr::lng_collectible_phone_copied(tr::now)
  178. : copyLink
  179. ? tr::lng_username_copied(tr::now)
  180. : tr::lng_username_text_copied(tr::now));
  181. };
  182. box->addRow(
  183. object_ptr<Ui::FlatLabel>(
  184. box,
  185. rpl::single(header),
  186. st::collectibleHeader),
  187. st::collectibleHeaderPadding
  188. )->setClickHandlerFilter([copyCallback](const auto &...) {
  189. copyCallback(false);
  190. return false;
  191. });
  192. box->addRow(MakeOwnerCell(box, info), st::collectibleOwnerPadding);
  193. const auto text = ((type == CollectibleType::Phone)
  194. ? tr::lng_collectible_phone_info
  195. : tr::lng_collectible_username_info)(
  196. tr::now,
  197. lt_date,
  198. TextWithEntities{ FormatDate(info.date) },
  199. lt_price,
  200. FormatPrice(info, details),
  201. Ui::Text::RichLangValue);
  202. const auto label = box->addRow(
  203. object_ptr<Ui::FlatLabel>(box, st::collectibleInfo),
  204. st::collectibleInfoPadding);
  205. label->setAttribute(Qt::WA_TransparentForMouseEvents);
  206. auto context = details.tonEmojiContext;
  207. context.repaint = [label] { label->update(); };
  208. label->setMarkedText(text, context);
  209. const auto more = box->addRow(
  210. object_ptr<Ui::RoundButton>(
  211. box,
  212. tr::lng_collectible_learn_more(),
  213. st::collectibleMore),
  214. st::collectibleMorePadding);
  215. more->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
  216. more->setClickedCallback([url = info.url] {
  217. File::OpenUrl(url);
  218. });
  219. const auto phrase = (type == CollectibleType::Phone)
  220. ? tr::lng_collectible_phone_copy
  221. : tr::lng_collectible_username_copy;
  222. auto owned = object_ptr<Ui::RoundButton>(
  223. box,
  224. phrase(),
  225. st::collectibleCopy);
  226. const auto copy = owned.data();
  227. copy->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
  228. copy->setClickedCallback([copyCallback] {
  229. copyCallback(true);
  230. });
  231. box->addButton(std::move(owned));
  232. box->setNoContentMargin(true);
  233. const auto buttonsParent = box->verticalLayout().get();
  234. const auto close = Ui::CreateChild<Ui::IconButton>(
  235. buttonsParent,
  236. st::boxTitleClose);
  237. close->setClickedCallback([=] {
  238. box->closeBox();
  239. });
  240. box->widthValue(
  241. ) | rpl::start_with_next([=](int width) {
  242. close->moveToRight(0, 0);
  243. }, box->lifetime());
  244. box->widthValue() | rpl::start_with_next([=](int width) {
  245. more->setFullWidth(width
  246. - st::collectibleMorePadding.left()
  247. - st::collectibleMorePadding.right());
  248. copy->setFullWidth(width
  249. - st::collectibleBox.buttonPadding.left()
  250. - st::collectibleBox.buttonPadding.right());
  251. }, box->lifetime());
  252. }
  253. } // namespace Ui