mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Implement collectible username / phone info boxes.
This commit is contained in:
parent
22f504ca21
commit
1061fb6c85
40 changed files with 630 additions and 75 deletions
BIN
Telegram/Resources/animations/collectible_phone.tgs
Normal file
BIN
Telegram/Resources/animations/collectible_phone.tgs
Normal file
Binary file not shown.
BIN
Telegram/Resources/animations/collectible_username.tgs
Normal file
BIN
Telegram/Resources/animations/collectible_username.tgs
Normal file
Binary file not shown.
|
@ -471,6 +471,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
"lng_bio_placeholder" = "Bio";
|
||||
|
||||
"lng_collectible_username_title" = "{username} is a collectible username that belongs to";
|
||||
"lng_collectible_username_info" = "This username was bought on **Fragment** on {date} for {price}";
|
||||
"lng_collectible_username_copy" = "Copy Link";
|
||||
"lng_collectible_phone_title" = "{phone} is a collectible phone number that belongs to";
|
||||
"lng_collectible_phone_info" = "This phone number was bought on **Fragment** on {date} for {price}";
|
||||
"lng_collectible_phone_copy" = "Copy Phone Number";
|
||||
"lng_collectible_learn_more" = "Learn More";
|
||||
|
||||
"lng_settings_section_info" = "My info";
|
||||
|
||||
"lng_settings_section_notify" = "Notifications";
|
||||
|
|
|
@ -22,5 +22,7 @@
|
|||
<file alias="hours.tgs">../../animations/hours.tgs</file>
|
||||
<file alias="phone.tgs">../../animations/phone.tgs</file>
|
||||
<file alias="chat_link.tgs">../../animations/chat_link.tgs</file>
|
||||
<file alias="collectible_username.tgs">../../animations/collectible_username.tgs</file>
|
||||
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -796,7 +796,7 @@ QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
|
|||
const auto storyId = story->fullId();
|
||||
const auto peer = story->peer();
|
||||
const auto fallback = [&] {
|
||||
const auto base = peer->userName();
|
||||
const auto base = peer->username();
|
||||
const auto story = QString::number(storyId.story);
|
||||
const auto query = base + "/s/" + story;
|
||||
return session().createInternalLinkFull(query);
|
||||
|
|
|
@ -1044,3 +1044,33 @@ inviteForbiddenTitle: FlatLabel(boxTitle) {
|
|||
inviteForbiddenTitlePadding: margins(32px, 4px, 32px, 0px);
|
||||
inviteForbiddenLockBg: dialogsUnreadBgMuted;
|
||||
inviteForbiddenLockIcon: icon {{ "emoji/premium_lock", dialogsUnreadFg }};
|
||||
|
||||
collectibleIconDiameter: 72px;
|
||||
collectibleIcon: 64px;
|
||||
collectibleIconPadding: margins(24px, 32px, 24px, 12px);
|
||||
collectibleHeader: FlatLabel(boxTitle) {
|
||||
minWidth: 120px;
|
||||
maxHeight: 0px;
|
||||
align: align(top);
|
||||
}
|
||||
collectibleHeaderPadding: margins(24px, 16px, 24px, 12px);
|
||||
collectibleOwnerPadding: margins(24px, 4px, 24px, 8px);
|
||||
collectibleInfo: inviteForbiddenInfo;
|
||||
collectibleInfoPadding: margins(24px, 12px, 24px, 12px);
|
||||
collectibleInfoTonMargins: margins(0px, 3px, 0px, 0px);
|
||||
collectibleMore: RoundButton(defaultActiveButton) {
|
||||
height: 36px;
|
||||
textTop: 9px;
|
||||
radius: 6px;
|
||||
}
|
||||
collectibleMorePadding: margins(24px, 12px, 24px, 0px);
|
||||
collectibleCopy: RoundButton(defaultLightButton) {
|
||||
height: 36px;
|
||||
textTop: 9px;
|
||||
radius: 6px;
|
||||
}
|
||||
collectibleBox: Box(defaultBox) {
|
||||
buttonPadding: margins(24px, 12px, 24px, 12px);
|
||||
buttonHeight: 36px;
|
||||
button: collectibleCopy;
|
||||
}
|
||||
|
|
|
@ -1789,10 +1789,10 @@ crl::time PeerListContent::paintRow(
|
|||
if (row->isSearchResult()
|
||||
&& !_mentionHighlight.isEmpty()
|
||||
&& peer
|
||||
&& peer->userName().startsWith(
|
||||
&& peer->username().startsWith(
|
||||
_mentionHighlight,
|
||||
Qt::CaseInsensitive)) {
|
||||
const auto username = peer->userName();
|
||||
const auto username = peer->username();
|
||||
const auto availableWidth = statusw;
|
||||
auto highlightedPart = '@' + username.mid(0, _mentionHighlight.size());
|
||||
auto grayedPart = username.mid(_mentionHighlight.size());
|
||||
|
|
|
@ -100,7 +100,7 @@ void Controller::prepare() {
|
|||
return;
|
||||
}
|
||||
auto row = std::make_unique<PeerListRow>(chat);
|
||||
const auto username = chat->userName();
|
||||
const auto username = chat->username();
|
||||
row->setCustomStatus(!username.isEmpty()
|
||||
? ('@' + username)
|
||||
: (chat->isChannel() && !chat->isMegagroup())
|
||||
|
|
|
@ -207,7 +207,7 @@ void ProcessFullPhoto(
|
|||
| UpdateFlag::Birthday)
|
||||
) | rpl::map([=] {
|
||||
const auto user = peer->asUser();
|
||||
const auto username = peer->userName();
|
||||
const auto username = peer->username();
|
||||
return PeerShortInfoFields{
|
||||
.name = peer->name(),
|
||||
.phone = user ? Ui::FormatPhone(user->phone()) : QString(),
|
||||
|
|
|
@ -322,7 +322,7 @@ void PublicsController::prepare() {
|
|||
auto &owner = _navigation->session().data();
|
||||
for (const auto &chat : chats) {
|
||||
if (const auto peer = owner.processChat(chat)) {
|
||||
if (!peer->isChannel() || peer->userName().isEmpty()) {
|
||||
if (!peer->isChannel() || peer->username().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
appendRow(peer);
|
||||
|
@ -346,7 +346,7 @@ void PublicsController::rowRightActionClicked(not_null<PeerListRow*> row) {
|
|||
const auto text = textMethod(
|
||||
tr::now,
|
||||
lt_link,
|
||||
peer->session().createInternalLink(peer->userName()),
|
||||
peer->session().createInternalLink(peer->username()),
|
||||
lt_group,
|
||||
peer->name());
|
||||
const auto confirmText = tr::lng_channels_too_much_public_revoke(
|
||||
|
@ -389,7 +389,7 @@ std::unique_ptr<PeerListRow> PublicsController::createRow(
|
|||
auto result = std::make_unique<PeerListRowWithLink>(peer);
|
||||
result->setActionLink(tr::lng_channels_too_much_public_revoke(tr::now));
|
||||
result->setCustomStatus(
|
||||
_navigation->session().createInternalLink(peer->userName()));
|
||||
_navigation->session().createInternalLink(peer->username()));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -897,6 +897,34 @@ bool ShowEditPersonalChannel(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ShowCollectiblePhone(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
const auto phone = match->captured(1);
|
||||
const auto peerId = PeerId(match->captured(2).toULongLong());
|
||||
controller->resolveCollectible(
|
||||
peerId,
|
||||
phone.startsWith('+') ? phone : '+' + phone);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShowCollectibleUsername(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
const auto username = match->captured(1);
|
||||
const auto peerId = PeerId(match->captured(2).toULongLong());
|
||||
controller->resolveCollectible(peerId, username);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExportTestChatTheme(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<const Data::CloudTheme*> theme) {
|
||||
|
@ -1299,6 +1327,14 @@ const std::vector<LocalUrlHandler> &InternalUrlHandlers() {
|
|||
u"^edit_personal_channel$"_q,
|
||||
ShowEditPersonalChannel,
|
||||
},
|
||||
{
|
||||
u"^collectible_phone/([\\+0-9\\-\\s]+)@([0-9]+)$"_q,
|
||||
ShowCollectiblePhone,
|
||||
},
|
||||
{
|
||||
u"^collectible_username/([a-zA-Z0-9\\-\\_\\.]+)@([0-9]+)$"_q,
|
||||
ShowCollectibleUsername,
|
||||
},
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -139,6 +139,10 @@ const std::vector<QString> &ChannelData::usernames() const {
|
|||
return _username.usernames();
|
||||
}
|
||||
|
||||
bool ChannelData::isUsernameEditable(QString username) const {
|
||||
return _username.isEditable(username);
|
||||
}
|
||||
|
||||
void ChannelData::setAccessHash(uint64 accessHash) {
|
||||
access = accessHash;
|
||||
input = MTP_inputPeerChannel(
|
||||
|
|
|
@ -182,6 +182,7 @@ public:
|
|||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
[[nodiscard]] bool isUsernameEditable(QString username) const;
|
||||
|
||||
[[nodiscard]] int membersCount() const {
|
||||
return std::max(_membersCount, 1);
|
||||
|
|
|
@ -940,7 +940,7 @@ const QString &PeerData::shortName() const {
|
|||
return _name;
|
||||
}
|
||||
|
||||
QString PeerData::userName() const {
|
||||
QString PeerData::username() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->username();
|
||||
} else if (const auto channel = asChannel()) {
|
||||
|
@ -949,6 +949,34 @@ QString PeerData::userName() const {
|
|||
return QString();
|
||||
}
|
||||
|
||||
QString PeerData::editableUsername() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->editableUsername();
|
||||
} else if (const auto channel = asChannel()) {
|
||||
return channel->editableUsername();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
const std::vector<QString> &PeerData::usernames() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->usernames();
|
||||
} else if (const auto channel = asChannel()) {
|
||||
return channel->usernames();
|
||||
}
|
||||
static const auto kEmpty = std::vector<QString>();
|
||||
return kEmpty;
|
||||
}
|
||||
|
||||
bool PeerData::isUsernameEditable(QString username) const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->isUsernameEditable(username);
|
||||
} else if (const auto channel = asChannel()) {
|
||||
return channel->isUsernameEditable(username);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerData::changeColorIndex(uint8 index) {
|
||||
index %= Ui::kColorIndexCount;
|
||||
if (_colorIndexCloud && _colorIndex == index) {
|
||||
|
|
|
@ -279,7 +279,11 @@ public:
|
|||
[[nodiscard]] const QString &name() const;
|
||||
[[nodiscard]] const QString &shortName() const;
|
||||
[[nodiscard]] const QString &topBarNameText() const;
|
||||
[[nodiscard]] QString userName() const;
|
||||
|
||||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
[[nodiscard]] bool isUsernameEditable(QString username) const;
|
||||
|
||||
[[nodiscard]] const base::flat_set<QString> &nameWords() const {
|
||||
return _nameWords;
|
||||
|
|
|
@ -1230,7 +1230,7 @@ PeerData *Session::peerByUsername(const QString &username) const {
|
|||
const auto uname = username.trimmed();
|
||||
for (const auto &[peerId, peer] : _peers) {
|
||||
if (peer->isLoaded()
|
||||
&& !peer->userName().compare(uname, Qt::CaseInsensitive)) {
|
||||
&& !peer->username().compare(uname, Qt::CaseInsensitive)) {
|
||||
return peer.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -303,7 +303,7 @@ void SponsoredMessages::append(
|
|||
? _session->data().processBotApp(peerId, *data.vapp())
|
||||
: nullptr;
|
||||
result.botLinkInfo = Window::PeerByLinkInfo{
|
||||
.usernameOrId = user->userName(),
|
||||
.usernameOrId = user->username(),
|
||||
.resolveType = botAppData
|
||||
? Window::ResolveType::BotApp
|
||||
: data.vstart_param()
|
||||
|
|
|
@ -450,7 +450,7 @@ bool Story::hasDirectLink() const {
|
|||
if (!_privacyPublic || (!_pinned && expired())) {
|
||||
return false;
|
||||
}
|
||||
return !_peer->userName().isEmpty();
|
||||
return !_peer->username().isEmpty();
|
||||
}
|
||||
|
||||
std::optional<QString> Story::errorTextForForward(
|
||||
|
|
|
@ -472,6 +472,10 @@ const std::vector<QString> &UserData::usernames() const {
|
|||
return _username.usernames();
|
||||
}
|
||||
|
||||
bool UserData::isUsernameEditable(QString username) const {
|
||||
return _username.isEditable(username);
|
||||
}
|
||||
|
||||
const QString &UserData::phone() const {
|
||||
return _phone;
|
||||
}
|
||||
|
|
|
@ -150,15 +150,11 @@ public:
|
|||
// a full check by canShareThisContact() call.
|
||||
[[nodiscard]] bool canShareThisContactFast() const;
|
||||
|
||||
MTPInputUser inputUser = MTP_inputUserEmpty();
|
||||
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
[[nodiscard]] const QString &phone() const;
|
||||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
QString nameOrPhone;
|
||||
[[nodiscard]] bool isUsernameEditable(QString username) const;
|
||||
|
||||
enum class ContactStatus : char {
|
||||
Unknown,
|
||||
|
@ -186,8 +182,6 @@ public:
|
|||
void setBirthday(Data::Birthday value);
|
||||
void setBirthday(const tl::conditional<MTPBirthday> &value);
|
||||
|
||||
std::unique_ptr<BotInfo> botInfo;
|
||||
|
||||
void setUnavailableReasons(
|
||||
std::vector<Data::UnavailableReason> &&reasons);
|
||||
|
||||
|
@ -209,6 +203,14 @@ public:
|
|||
[[nodiscard]] MsgId personalChannelMessageId() const;
|
||||
void setPersonalChannel(ChannelId channelId, MsgId messageId);
|
||||
|
||||
MTPInputUser inputUser = MTP_inputUserEmpty();
|
||||
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
QString nameOrPhone;
|
||||
|
||||
std::unique_ptr<BotInfo> botInfo;
|
||||
|
||||
private:
|
||||
auto unavailableReasons() const
|
||||
-> const std::vector<Data::UnavailableReason> & override;
|
||||
|
|
|
@ -80,4 +80,10 @@ const std::vector<QString> &UsernamesInfo::usernames() const {
|
|||
return _usernames;
|
||||
}
|
||||
|
||||
bool UsernamesInfo::isEditable(const QString &username) const {
|
||||
return (_indexEditableUsername >= 0)
|
||||
&& (_indexEditableUsername < _usernames.size())
|
||||
&& (_usernames[_indexEditableUsername] == username);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
[[nodiscard]] bool isEditable(const QString &username) const;
|
||||
|
||||
private:
|
||||
std::vector<QString> _usernames;
|
||||
|
|
|
@ -1087,7 +1087,7 @@ void InnerWidget::paintPeerSearchResult(
|
|||
|
||||
QRect tr(context.st->textLeft, context.st->textTop, namewidth, st::dialogsTextFont->height);
|
||||
p.setFont(st::dialogsTextFont);
|
||||
QString username = peer->userName();
|
||||
QString username = peer->username();
|
||||
if (!context.active && username.startsWith(_peerSearchQuery, Qt::CaseInsensitive)) {
|
||||
auto first = '@' + username.mid(0, _peerSearchQuery.size());
|
||||
auto second = username.mid(_peerSearchQuery.size());
|
||||
|
@ -4021,7 +4021,7 @@ void InnerWidget::setupShortcuts() {
|
|||
const auto history = thread->owningHistory();
|
||||
const auto isArchived = history->folder()
|
||||
&& (history->folder()->id() == Data::Folder::kId);
|
||||
|
||||
|
||||
Window::ToggleHistoryArchived(
|
||||
_controller->uiShow(),
|
||||
history,
|
||||
|
|
|
@ -496,7 +496,7 @@ auto GenerateParticipantString(
|
|||
data,
|
||||
});
|
||||
}
|
||||
const auto username = peer->userName();
|
||||
const auto username = peer->username();
|
||||
if (username.isEmpty()) {
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -217,7 +217,18 @@ void AddRecipient(not_null<Ui::GenericBox*> box, const TextWithEntities &t) {
|
|||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] QImage IconCurrency(
|
||||
[[nodiscard]] QString FormatDate(const QDateTime &date) {
|
||||
return tr::lng_group_call_starts_short_date(
|
||||
tr::now,
|
||||
lt_date,
|
||||
langDayOfMonth(date.date()),
|
||||
lt_time,
|
||||
QLocale().toString(date.time(), QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QImage IconCurrency(
|
||||
const style::FlatLabel &label,
|
||||
const QColor &c) {
|
||||
const auto s = Size(label.style.font->ascent);
|
||||
|
@ -234,17 +245,6 @@ void AddRecipient(not_null<Ui::GenericBox*> box, const TextWithEntities &t) {
|
|||
return image;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatDate(const QDateTime &date) {
|
||||
return tr::lng_group_call_starts_short_date(
|
||||
tr::now,
|
||||
lt_date,
|
||||
langDayOfMonth(date.date()),
|
||||
lt_time,
|
||||
QLocale().toString(date.time(), QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
InnerWidget::InnerWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller,
|
||||
|
|
|
@ -23,6 +23,10 @@ namespace Info::ChannelEarn {
|
|||
|
||||
class Memento;
|
||||
|
||||
[[nodiscard]] QImage IconCurrency(
|
||||
const style::FlatLabel &label,
|
||||
const QColor &c);
|
||||
|
||||
class InnerWidget final : public Ui::VerticalLayout {
|
||||
public:
|
||||
struct ShowRequest final {
|
||||
|
|
|
@ -116,16 +116,25 @@ base::options::toggle ShowPeerIdBelowAbout({
|
|||
|
||||
[[nodiscard]] Fn<void(QString)> UsernamesLinkCallback(
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &addToLink) {
|
||||
const auto weak = base::make_weak(controller);
|
||||
return [=](QString link) {
|
||||
if (!link.startsWith(u"https://"_q)) {
|
||||
link = peer->session().createInternalLinkFull(peer->userName())
|
||||
if (link.startsWith(u"internal:"_q)) {
|
||||
Core::App().openInternalUrl(link,
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = weak,
|
||||
}));
|
||||
return;
|
||||
} else if (!link.startsWith(u"https://"_q)) {
|
||||
link = peer->session().createInternalLinkFull(peer->username())
|
||||
+ addToLink;
|
||||
}
|
||||
if (!link.isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
show->showToast(tr::lng_username_copied(tr::now));
|
||||
if (const auto window = weak.get()) {
|
||||
window->showToast(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1041,16 +1050,13 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
UsernameValue(user, true) | rpl::map([=](TextWithEntities u) {
|
||||
return u.text.isEmpty()
|
||||
? TextWithEntities()
|
||||
: Ui::Text::Link(
|
||||
u,
|
||||
user->session().createInternalLinkFull(
|
||||
u.text.mid(1)));
|
||||
: Ui::Text::Link(u, UsernameUrl(user, u.text.mid(1)));
|
||||
}),
|
||||
QString(),
|
||||
st::infoProfileLabeledUsernamePadding);
|
||||
const auto callback = UsernamesLinkCallback(
|
||||
_peer,
|
||||
controller->uiShow(),
|
||||
controller,
|
||||
QString());
|
||||
const auto hook = [=](Ui::FlatLabel::ContextMenuRequest request) {
|
||||
if (!request.link) {
|
||||
|
@ -1094,7 +1100,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
}, copyUsername->lifetime());
|
||||
copyUsername->setClickedCallback([=] {
|
||||
const auto link = user->session().createInternalLinkFull(
|
||||
user->userName());
|
||||
user->username());
|
||||
if (!link.isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
controller->showToast(tr::lng_username_copied(tr::now));
|
||||
|
@ -1159,7 +1165,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
const auto controller = _controller->parentController();
|
||||
const auto linkCallback = UsernamesLinkCallback(
|
||||
_peer,
|
||||
controller->uiShow(),
|
||||
controller,
|
||||
addToLink);
|
||||
linkLine.text->overrideLinkClickHandler(linkCallback);
|
||||
linkLine.subtext->overrideLinkClickHandler(linkCallback);
|
||||
|
|
|
@ -112,23 +112,22 @@ int TextItem::contentHeight() const {
|
|||
|
||||
} // namespace
|
||||
|
||||
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user) {
|
||||
if (user->isSelf()) {
|
||||
return;
|
||||
}
|
||||
bool IsCollectiblePhone(not_null<UserData*> user) {
|
||||
using Strings = std::vector<QString>;
|
||||
const auto prefixes = user->session().appConfig().get<Strings>(
|
||||
u"fragment_prefixes"_q,
|
||||
std::vector<QString>());
|
||||
{
|
||||
const auto proj = [&phone = user->phone()](const QString &p) {
|
||||
return phone.startsWith(p);
|
||||
};
|
||||
if (ranges::none_of(prefixes, proj)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (const auto url = AppConfig::FragmentLink(&user->session())) {
|
||||
Strings{ u"888"_q });
|
||||
const auto phone = user->phone();
|
||||
const auto proj = [&](const QString &p) {
|
||||
return phone.startsWith(p);
|
||||
};
|
||||
return ranges::any_of(prefixes, proj);
|
||||
}
|
||||
|
||||
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user) {
|
||||
if (user->isSelf() || !IsCollectiblePhone(user)) {
|
||||
return;
|
||||
} else if (const auto url = AppConfig::FragmentLink(&user->session())) {
|
||||
menu->addSeparator(&st::expandedMenuSeparator);
|
||||
const auto link = Ui::Text::Link(
|
||||
tr::lng_info_mobile_context_menu_fragment_about_link(tr::now),
|
||||
|
|
|
@ -16,6 +16,8 @@ class PopupMenu;
|
|||
namespace Info {
|
||||
namespace Profile {
|
||||
|
||||
[[nodiscard]] bool IsCollectiblePhone(not_null<UserData*> user);
|
||||
|
||||
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user);
|
||||
|
||||
} // namespace Profile
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "apiwrap.h"
|
||||
#include "info/profile/info_profile_phone_menu.h"
|
||||
#include "info/profile/info_profile_badge.h"
|
||||
#include "core/application.h"
|
||||
#include "core/click_handler_types.h"
|
||||
|
@ -55,7 +56,7 @@ auto PlainUsernameValue(not_null<PeerData*> peer) {
|
|||
peer->session().changes().peerFlagsValue(peer, UpdateFlag::Username),
|
||||
peer->session().changes().peerFlagsValue(peer, UpdateFlag::Usernames)
|
||||
) | rpl::map([=] {
|
||||
return peer->userName();
|
||||
return peer->username();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -136,14 +137,19 @@ rpl::producer<TextWithEntities> PhoneOrHiddenValue(not_null<UserData*> user) {
|
|||
PlainUsernameValue(user),
|
||||
PlainAboutValue(user),
|
||||
tr::lng_info_mobile_hidden()
|
||||
) | rpl::map([](
|
||||
) | rpl::map([user](
|
||||
const TextWithEntities &phone,
|
||||
const QString &username,
|
||||
const QString &about,
|
||||
const QString &hidden) {
|
||||
return (phone.text.isEmpty() && username.isEmpty() && about.isEmpty())
|
||||
? Ui::Text::WithEntities(hidden)
|
||||
: phone;
|
||||
if (phone.text.isEmpty() && username.isEmpty() && about.isEmpty()) {
|
||||
return Ui::Text::WithEntities(hidden);
|
||||
} else if (IsCollectiblePhone(user)) {
|
||||
return Ui::Text::Link(phone, u"internal:collectible_phone/"_q
|
||||
+ user->phone() + '@' + QString::number(user->id.value));
|
||||
} else {
|
||||
return phone;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -160,15 +166,22 @@ rpl::producer<TextWithEntities> UsernameValue(
|
|||
}) | Ui::Text::ToWithEntities();
|
||||
}
|
||||
|
||||
QString UsernameUrl(not_null<PeerData*> peer, const QString &username) {
|
||||
return peer->isUsernameEditable(username)
|
||||
? peer->session().createInternalLinkFull(username)
|
||||
: (u"internal:collectible_username/"_q
|
||||
+ username
|
||||
+ "@"
|
||||
+ QString::number(peer->id.value));
|
||||
}
|
||||
|
||||
rpl::producer<std::vector<TextWithEntities>> UsernamesValue(
|
||||
not_null<PeerData*> peer) {
|
||||
const auto map = [=](const std::vector<QString> &usernames) {
|
||||
return ranges::views::all(
|
||||
usernames
|
||||
) | ranges::views::transform([&](const QString &u) {
|
||||
return Ui::Text::Link(
|
||||
u,
|
||||
peer->session().createInternalLinkFull(u));
|
||||
return Ui::Text::Link(u, UsernameUrl(peer, u));
|
||||
}) | ranges::to_vector;
|
||||
};
|
||||
auto value = rpl::merge(
|
||||
|
@ -224,9 +237,7 @@ rpl::producer<QString> LinkValue(not_null<PeerData*> peer, bool primary) {
|
|||
? PlainPrimaryUsernameValue(peer)
|
||||
: PlainUsernameValue(peer) | rpl::type_erased()
|
||||
) | rpl::map([=](QString &&username) {
|
||||
return username.isEmpty()
|
||||
? QString()
|
||||
: peer->session().createInternalLinkFull(username);
|
||||
return username.isEmpty() ? QString() : UsernameUrl(peer, username);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,9 @@ rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
|
|||
bool primary = false);
|
||||
[[nodiscard]] rpl::producer<std::vector<TextWithEntities>> UsernamesValue(
|
||||
not_null<PeerData*> peer);
|
||||
[[nodiscard]] QString UsernameUrl(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &username);
|
||||
[[nodiscard]] TextWithEntities AboutWithEntities(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &value);
|
||||
|
|
|
@ -53,7 +53,7 @@ Domain::Domain(const QString &dataName)
|
|||
: rpl::never<Data::PeerUpdate>();
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::start_with_next([](const Data::PeerUpdate &update) {
|
||||
CrashReports::SetAnnotation("Username", update.peer->userName());
|
||||
CrashReports::SetAnnotation("Username", update.peer->username());
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ void Cover::initViewers() {
|
|||
}, lifetime());
|
||||
|
||||
_username->overrideLinkClickHandler([=] {
|
||||
const auto username = _user->userName();
|
||||
const auto username = _user->username();
|
||||
if (username.isEmpty()) {
|
||||
_controller->show(Box(UsernamesBox, _user));
|
||||
} else {
|
||||
|
|
271
Telegram/SourceFiles/ui/boxes/collectible_info_box.cpp
Normal file
271
Telegram/SourceFiles/ui/boxes/collectible_info_box.cpp
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
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
|
45
Telegram/SourceFiles/ui/boxes/collectible_info_box.h
Normal file
45
Telegram/SourceFiles/ui/boxes/collectible_info_box.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class GenericBox;
|
||||
class DynamicImage;
|
||||
|
||||
enum class CollectibleType {
|
||||
Phone,
|
||||
Username,
|
||||
};
|
||||
|
||||
[[nodiscard]] CollectibleType DetectCollectibleType(const QString &entity);
|
||||
|
||||
struct CollectibleInfo {
|
||||
QString entity;
|
||||
QString copyText;
|
||||
std::shared_ptr<DynamicImage> ownerUserpic;
|
||||
QString ownerName;
|
||||
uint64 cryptoAmount = 0;
|
||||
uint64 amount = 0;
|
||||
QString cryptoCurrency;
|
||||
QString currency;
|
||||
QString url;
|
||||
TimeId date = 0;
|
||||
};
|
||||
|
||||
struct CollectibleDetails {
|
||||
TextWithEntities tonEmoji;
|
||||
Fn<std::any()> tonEmojiContext;
|
||||
};
|
||||
|
||||
void CollectibleInfoBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
CollectibleInfo info,
|
||||
CollectibleDetails details);
|
||||
|
||||
} // namespace Ui
|
|
@ -357,6 +357,7 @@ CurrencyRule LookupCurrencyRule(const QString ¤cy) {
|
|||
|
||||
char do_decimal_point() const override { return decimal; }
|
||||
char do_thousands_sep() const override { return thousands; }
|
||||
std::string do_grouping() const override { return "\3"; }
|
||||
|
||||
char decimal = '.';
|
||||
char thousands = ',';
|
||||
|
|
|
@ -135,8 +135,8 @@ not_null<Ui::SettingsButton*> AddMyChannelsBox(
|
|||
const auto count = c ? c->membersCount() : g->count;
|
||||
_status.setText(
|
||||
st::defaultTextStyle,
|
||||
!p->userName().isEmpty()
|
||||
? ('@' + p->userName())
|
||||
!p->username().isEmpty()
|
||||
? ('@' + p->username())
|
||||
: count
|
||||
? tr::lng_chat_status_subscribers(
|
||||
tr::now,
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/delete_messages_box.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_filters_menu.h"
|
||||
#include "info/channel_statistics/earn/info_earn_inner_widget.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "inline_bots/bot_attach_web_view.h"
|
||||
|
@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_scheduled_section.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "media/view/media_view_open_common.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_document_resolver.h"
|
||||
#include "data/data_download_manager.h"
|
||||
#include "data/data_session.h"
|
||||
|
@ -49,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/shortcuts.h"
|
||||
#include "core/application.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
|
@ -62,7 +65,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "calls/calls_instance.h" // Core::App().calls().inCall().
|
||||
#include "calls/group/calls_group_call.h"
|
||||
#include "ui/boxes/calendar_box.h"
|
||||
#include "ui/boxes/collectible_info_box.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "mainwidget.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_domain.h"
|
||||
|
@ -83,6 +88,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_premium.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_layers.h" // st::boxLabel
|
||||
|
||||
|
@ -155,6 +161,47 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] Ui::CollectibleDetails PrepareCollectibleDetails(
|
||||
not_null<Main::Session*> session) {
|
||||
const auto makeContext = [=] {
|
||||
return Core::MarkedTextContext{
|
||||
.session = session,
|
||||
.customEmojiRepaint = [] {},
|
||||
};
|
||||
};
|
||||
return {
|
||||
.tonEmoji = Ui::Text::SingleCustomEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
Info::ChannelEarn::IconCurrency(
|
||||
st::collectibleInfo,
|
||||
st::collectibleInfo.textFg->c),
|
||||
st::collectibleInfoTonMargins,
|
||||
true)),
|
||||
.tonEmojiContext = makeContext,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] Ui::CollectibleInfo Parse(
|
||||
const QString &entity,
|
||||
not_null<PeerData*> owner,
|
||||
const MTPfragment_CollectibleInfo &info) {
|
||||
const auto &data = info.data();
|
||||
return {
|
||||
.entity = entity,
|
||||
.copyText = (entity.startsWith('+')
|
||||
? QString()
|
||||
: owner->session().createInternalLinkFull(entity)),
|
||||
.ownerUserpic = Ui::MakeUserpicThumbnail(owner, true),
|
||||
.ownerName = owner->name(),
|
||||
.cryptoAmount = data.vcrypto_amount().v,
|
||||
.amount = data.vamount().v,
|
||||
.cryptoCurrency = qs(data.vcrypto_currency()),
|
||||
.currency = qs(data.vcurrency()),
|
||||
.url = qs(data.vurl()),
|
||||
.date = data.vpurchase_date().v,
|
||||
};
|
||||
}
|
||||
|
||||
MainWindowShow::MainWindowShow(not_null<SessionController*> controller)
|
||||
: _window(base::make_weak(controller)) {
|
||||
}
|
||||
|
@ -688,6 +735,36 @@ void SessionNavigation::resolveBoostState(not_null<ChannelData*> channel) {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void SessionNavigation::resolveCollectible(
|
||||
PeerId ownerId,
|
||||
const QString &entity,
|
||||
Fn<void(QString)> fail) {
|
||||
if (_collectibleEntity == entity) {
|
||||
return;
|
||||
} else {
|
||||
_api.request(base::take(_collectibleRequestId)).cancel();
|
||||
}
|
||||
_collectibleEntity = entity;
|
||||
_collectibleRequestId = _api.request(MTPfragment_GetCollectibleInfo(
|
||||
((Ui::DetectCollectibleType(entity) == Ui::CollectibleType::Phone)
|
||||
? MTP_inputCollectiblePhone(MTP_string(entity))
|
||||
: MTP_inputCollectibleUsername(MTP_string(entity)))
|
||||
)).done([=](const MTPfragment_CollectibleInfo &result) {
|
||||
const auto entity = base::take(_collectibleEntity);
|
||||
_collectibleRequestId = 0;
|
||||
uiShow()->show(Box(
|
||||
Ui::CollectibleInfoBox,
|
||||
Parse(entity, _session->data().peer(ownerId), result),
|
||||
PrepareCollectibleDetails(_session)));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_collectibleEntity = QString();
|
||||
_collectibleRequestId = 0;
|
||||
if (fail) {
|
||||
fail(error.type());
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void SessionNavigation::applyBoost(
|
||||
not_null<ChannelData*> channel,
|
||||
Fn<void(Ui::BoostCounters)> done) {
|
||||
|
|
|
@ -245,6 +245,11 @@ public:
|
|||
|
||||
void resolveBoostState(not_null<ChannelData*> channel);
|
||||
|
||||
void resolveCollectible(
|
||||
PeerId ownerId,
|
||||
const QString &entity,
|
||||
Fn<void(QString)> fail = nullptr);
|
||||
|
||||
base::weak_ptr<Ui::Toast::Instance> showToast(
|
||||
Ui::Toast::Config &&config);
|
||||
base::weak_ptr<Ui::Toast::Instance> showToast(
|
||||
|
@ -304,6 +309,9 @@ private:
|
|||
|
||||
ChannelData *_boostStateResolving = nullptr;
|
||||
|
||||
QString _collectibleEntity;
|
||||
mtpRequestId _collectibleRequestId = 0;
|
||||
|
||||
};
|
||||
|
||||
class SessionController : public SessionNavigation {
|
||||
|
|
|
@ -240,6 +240,8 @@ PRIVATE
|
|||
ui/boxes/choose_language_box.h
|
||||
ui/boxes/choose_time.cpp
|
||||
ui/boxes/choose_time.h
|
||||
ui/boxes/collectible_info_box.cpp
|
||||
ui/boxes/collectible_info_box.h
|
||||
ui/boxes/confirm_box.cpp
|
||||
ui/boxes/confirm_box.h
|
||||
ui/boxes/confirm_phone_box.cpp
|
||||
|
|
Loading…
Add table
Reference in a new issue