272 lines
8.0 KiB
C++
272 lines
8.0 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "ui/boxes/collectible_info_box.h"
|
|
|
|
#include "base/unixtime.h"
|
|
#include "core/file_utilities.h"
|
|
#include "lang/lang_keys.h"
|
|
#include "lottie/lottie_icon.h"
|
|
#include "info/channel_statistics/earn/earn_format.h"
|
|
#include "ui/layers/generic_box.h"
|
|
#include "ui/text/format_values.h"
|
|
#include "ui/text/text_utilities.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/widgets/labels.h"
|
|
#include "ui/dynamic_image.h"
|
|
#include "ui/painter.h"
|
|
#include "settings/settings_common.h"
|
|
#include "styles/style_boxes.h"
|
|
#include "styles/style_layers.h"
|
|
|
|
#include <QtCore/QRegularExpression>
|
|
#include <QtGui/QGuiApplication>
|
|
|
|
namespace Ui {
|
|
namespace {
|
|
|
|
constexpr auto kTonMultiplier = uint64(1000000000);
|
|
|
|
[[nodiscard]] QString FormatEntity(CollectibleType type, QString entity) {
|
|
switch (type) {
|
|
case CollectibleType::Phone: {
|
|
static const auto kNonDigits = QRegularExpression(u"[^\\d]"_q);
|
|
entity.replace(kNonDigits, QString());
|
|
} return Ui::FormatPhone(entity);
|
|
case CollectibleType::Username:
|
|
return entity.startsWith('@') ? entity : ('@' + entity);
|
|
}
|
|
Unexpected("CollectibleType in FormatEntity.");
|
|
}
|
|
|
|
[[nodiscard]] QString FormatDate(TimeId date) {
|
|
return langDateTime(base::unixtime::parse(date));
|
|
}
|
|
|
|
[[nodiscard]] TextWithEntities FormatPrice(
|
|
const CollectibleInfo &info,
|
|
const CollectibleDetails &details) {
|
|
auto minor = Info::ChannelEarn::MinorPart(info.cryptoAmount);
|
|
if (minor.size() == 1 && minor.at(0) == '.') {
|
|
minor += '0';
|
|
}
|
|
auto price = (info.cryptoCurrency == u"TON"_q)
|
|
? base::duplicate(
|
|
details.tonEmoji
|
|
).append(
|
|
Info::ChannelEarn::MajorPart(info.cryptoAmount)
|
|
).append(minor)
|
|
: TextWithEntities{ ('{'
|
|
+ info.cryptoCurrency + ':' + QString::number(info.cryptoAmount)
|
|
+ '}') };
|
|
const auto fiat = Ui::FillAmountAndCurrency(info.amount, info.currency);
|
|
return Ui::Text::Wrapped(
|
|
price,
|
|
EntityType::Bold
|
|
).append(u" ("_q + fiat + ')');
|
|
}
|
|
|
|
[[nodiscard]] object_ptr<Ui::RpWidget> MakeOwnerCell(
|
|
not_null<QWidget*> parent,
|
|
const CollectibleInfo &info) {
|
|
const auto st = &st::defaultMultiSelectItem;
|
|
const auto size = st->height;
|
|
auto result = object_ptr<Ui::FixedHeightWidget>(parent.get(), size);
|
|
const auto raw = result.data();
|
|
|
|
const auto name = info.ownerName;
|
|
const auto userpic = info.ownerUserpic;
|
|
const auto nameWidth = st->style.font->width(name);
|
|
const auto added = size + st->padding.left() + st->padding.right();
|
|
const auto subscribed = std::make_shared<bool>(false);
|
|
raw->paintRequest() | rpl::start_with_next([=] {
|
|
const auto use = std::min(nameWidth + added, raw->width());
|
|
const auto x = (raw->width() - use) / 2;
|
|
if (const auto available = use - added; available > 0) {
|
|
auto p = QPainter(raw);
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(st->textBg);
|
|
p.drawRoundedRect(x, 0, use, size, size / 2., size / 2.);
|
|
|
|
if (!*subscribed) {
|
|
*subscribed = true;
|
|
userpic->subscribeToUpdates([=] { raw->update(); });
|
|
}
|
|
p.drawImage(QRect(x, 0, size, size), userpic->image(size));
|
|
|
|
const auto textx = x + size + st->padding.left();
|
|
const auto texty = st->padding.top() + st->style.font->ascent;
|
|
const auto text = (use == nameWidth + added)
|
|
? name
|
|
: st->style.font->elided(name, available);
|
|
p.setPen(st->textFg);
|
|
p.setFont(st->style.font);
|
|
p.drawText(textx, texty, text);
|
|
}
|
|
}, raw->lifetime());
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CollectibleType DetectCollectibleType(const QString &entity) {
|
|
return entity.startsWith('+')
|
|
? CollectibleType::Phone
|
|
: CollectibleType::Username;
|
|
}
|
|
|
|
void CollectibleInfoBox(
|
|
not_null<Ui::GenericBox*> box,
|
|
CollectibleInfo info,
|
|
CollectibleDetails details) {
|
|
box->setWidth(st::boxWideWidth);
|
|
box->setStyle(st::collectibleBox);
|
|
|
|
const auto type = DetectCollectibleType(info.entity);
|
|
|
|
const auto icon = box->addRow(
|
|
object_ptr<Ui::FixedHeightWidget>(box, st::collectibleIconDiameter),
|
|
st::collectibleIconPadding);
|
|
icon->paintRequest(
|
|
) | rpl::start_with_next([=](QRect clip) {
|
|
const auto size = icon->height();
|
|
const auto inner = QRect(
|
|
(icon->width() - size) / 2,
|
|
0,
|
|
size,
|
|
size);
|
|
if (!inner.intersects(clip)) {
|
|
return;
|
|
}
|
|
auto p = QPainter(icon);
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
p.setBrush(st::defaultActiveButton.textBg);
|
|
p.setPen(Qt::NoPen);
|
|
p.drawEllipse(inner);
|
|
}, icon->lifetime());
|
|
const auto lottieSize = st::collectibleIcon;
|
|
auto lottie = Settings::CreateLottieIcon(
|
|
icon,
|
|
{
|
|
.name = (type == CollectibleType::Phone
|
|
? u"collectible_phone"_q
|
|
: u"collectible_username"_q),
|
|
.color = &st::defaultActiveButton.textFg,
|
|
.sizeOverride = { lottieSize, lottieSize },
|
|
},
|
|
QMargins());
|
|
box->showFinishes(
|
|
) | rpl::start_with_next([animate = std::move(lottie.animate)] {
|
|
animate(anim::repeat::once);
|
|
}, box->lifetime());
|
|
const auto animation = lottie.widget.release();
|
|
icon->sizeValue() | rpl::start_with_next([=](QSize size) {
|
|
const auto skip = (type == CollectibleType::Phone)
|
|
? style::ConvertScale(2)
|
|
: 0;
|
|
animation->move(
|
|
(size.width() - animation->width()) / 2,
|
|
skip + (size.height() - animation->height()) / 2);
|
|
}, animation->lifetime());
|
|
|
|
const auto formatted = FormatEntity(type, info.entity);
|
|
const auto header = (type == CollectibleType::Phone)
|
|
? tr::lng_collectible_phone_title(
|
|
tr::now,
|
|
lt_phone,
|
|
Ui::Text::Link(formatted),
|
|
Ui::Text::WithEntities)
|
|
: tr::lng_collectible_username_title(
|
|
tr::now,
|
|
lt_username,
|
|
Ui::Text::Link(formatted),
|
|
Ui::Text::WithEntities);
|
|
const auto copyCallback = [box, type, formatted, text = info.copyText] {
|
|
QGuiApplication::clipboard()->setText(
|
|
text.isEmpty() ? formatted : text);
|
|
box->uiShow()->showToast((type == CollectibleType::Phone)
|
|
? tr::lng_text_copied(tr::now)
|
|
: tr::lng_username_copied(tr::now));
|
|
};
|
|
box->addRow(
|
|
object_ptr<Ui::FlatLabel>(
|
|
box,
|
|
rpl::single(header),
|
|
st::collectibleHeader),
|
|
st::collectibleHeaderPadding
|
|
)->setClickHandlerFilter([copyCallback](const auto &...) {
|
|
copyCallback();
|
|
return false;
|
|
});
|
|
|
|
box->addRow(MakeOwnerCell(box, info), st::collectibleOwnerPadding);
|
|
|
|
const auto text = ((type == CollectibleType::Phone)
|
|
? tr::lng_collectible_phone_info
|
|
: tr::lng_collectible_username_info)(
|
|
tr::now,
|
|
lt_date,
|
|
TextWithEntities{ FormatDate(info.date) },
|
|
lt_price,
|
|
FormatPrice(info, details),
|
|
Ui::Text::RichLangValue);
|
|
const auto label = box->addRow(
|
|
object_ptr<Ui::FlatLabel>(box, st::collectibleInfo),
|
|
st::collectibleInfoPadding);
|
|
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
label->setMarkedText(text, details.tonEmojiContext());
|
|
|
|
const auto more = box->addRow(
|
|
object_ptr<Ui::RoundButton>(
|
|
box,
|
|
tr::lng_collectible_learn_more(),
|
|
st::collectibleMore),
|
|
st::collectibleMorePadding);
|
|
more->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
|
more->setClickedCallback([url = info.url] {
|
|
File::OpenUrl(url);
|
|
});
|
|
|
|
const auto phrase = (type == CollectibleType::Phone)
|
|
? tr::lng_collectible_phone_copy
|
|
: tr::lng_collectible_username_copy;
|
|
auto owned = object_ptr<Ui::RoundButton>(
|
|
box,
|
|
phrase(),
|
|
st::collectibleCopy);
|
|
const auto copy = owned.data();
|
|
copy->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
|
copy->setClickedCallback(copyCallback);
|
|
box->addButton(std::move(owned));
|
|
|
|
box->setNoContentMargin(true);
|
|
const auto buttonsParent = box->verticalLayout().get();
|
|
const auto close = Ui::CreateChild<Ui::IconButton>(
|
|
buttonsParent,
|
|
st::boxTitleClose);
|
|
close->setClickedCallback([=] {
|
|
box->closeBox();
|
|
});
|
|
box->widthValue(
|
|
) | rpl::start_with_next([=](int width) {
|
|
close->moveToRight(0, 0);
|
|
}, box->lifetime());
|
|
|
|
box->widthValue() | rpl::start_with_next([=](int width) {
|
|
more->setFullWidth(width
|
|
- st::collectibleMorePadding.left()
|
|
- st::collectibleMorePadding.right());
|
|
copy->setFullWidth(width
|
|
- st::collectibleBox.buttonPadding.left()
|
|
- st::collectibleBox.buttonPadding.right());
|
|
}, box->lifetime());
|
|
}
|
|
|
|
} // namespace Ui
|