mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 15:17:07 +02:00
Implement unique gift transfer.
This commit is contained in:
parent
7491337bfd
commit
083400d1c2
19 changed files with 622 additions and 65 deletions
Telegram
|
@ -332,6 +332,8 @@ PRIVATE
|
|||
boxes/sticker_set_box.h
|
||||
boxes/stickers_box.cpp
|
||||
boxes/stickers_box.h
|
||||
boxes/transfer_gift_box.cpp
|
||||
boxes/transfer_gift_box.h
|
||||
boxes/translate_box.cpp
|
||||
boxes/translate_box.h
|
||||
boxes/url_auth_box.cpp
|
||||
|
|
|
@ -2025,6 +2025,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_action_gift_unique_sent" = "You sent a unique collectible item";
|
||||
"lng_action_gift_upgraded" = "{user} turned the gift from you to a unique collectible";
|
||||
"lng_action_gift_upgraded_mine" = "You turned the gift from {user} to a unique collectible";
|
||||
"lng_action_gift_transferred" = "{user} transferred you a gift";
|
||||
"lng_action_gift_transferred_mine" = "You transferred a gift to {user}";
|
||||
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
|
||||
"lng_action_gift_for_stars#one" = "{count} Star";
|
||||
"lng_action_gift_for_stars#other" = "{count} Stars";
|
||||
|
@ -3239,6 +3241,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_gift_sent_about#other" = "You spent **{count}** Stars from your balance.";
|
||||
"lng_gift_limited_of_one" = "unique";
|
||||
"lng_gift_limited_of_count" = "1 of {amount}";
|
||||
"lng_gift_collectible_tag" = "gift";
|
||||
"lng_gift_price_unique" = "Unique";
|
||||
"lng_gift_view_unpack" = "Unpack";
|
||||
"lng_gift_anonymous_hint" = "Only you can see the sender's name.";
|
||||
|
|
|
@ -806,8 +806,8 @@ std::optional<Data::StarGift> FromTL(
|
|||
.id = uint64(data.vid().v),
|
||||
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
|
||||
.title = qs(data.vtitle()),
|
||||
.number = data.vnum().v,
|
||||
.ownerId = peerFromUser(UserId(data.vowner_id().v)),
|
||||
.number = data.vnum().v,
|
||||
.model = *model,
|
||||
.pattern = *pattern,
|
||||
}),
|
||||
|
@ -837,6 +837,9 @@ std::optional<Data::UserStarGift> FromTL(
|
|||
auto parsed = FromTL(session, data.vgift());
|
||||
if (!parsed) {
|
||||
return {};
|
||||
} else if (const auto unique = parsed->unique.get()) {
|
||||
unique->starsForTransfer = data.vtransfer_stars().value_or(-1);
|
||||
unique->exportAt = data.vcan_export_at().value_or_empty();
|
||||
}
|
||||
return Data::UserStarGift{
|
||||
.info = std::move(*parsed),
|
||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/peers/replace_boost_box.h" // BoostsForGift.
|
||||
#include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox.
|
||||
#include "boxes/star_gift_box.h" // ShowStarGiftBox.
|
||||
#include "boxes/transfer_gift_box.h" // ShowTransferGiftBox.
|
||||
#include "data/data_boosts.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
|
@ -1204,8 +1205,16 @@ void AddStarGiftTable(
|
|||
const auto unique = entry.uniqueGift.get();
|
||||
if (unique) {
|
||||
const auto ownerId = PeerId(entry.bareGiftOwnerId);
|
||||
auto send = entry.in ? tr::lng_gift_unique_owner_change() : nullptr;
|
||||
auto handler = entry.in ? Fn<void()>([=] {}) : nullptr;
|
||||
const auto transfer = entry.in
|
||||
&& entry.bareMsgId
|
||||
&& (unique->starsForTransfer >= 0);
|
||||
auto send = transfer ? tr::lng_gift_unique_owner_change() : nullptr;
|
||||
auto handler = transfer ? Fn<void()>([=] {
|
||||
ShowTransferGiftBox(
|
||||
controller->parentController(),
|
||||
entry.uniqueGift,
|
||||
MsgId(entry.bareMsgId));
|
||||
}) : nullptr;
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_owner(),
|
||||
|
|
|
@ -1129,11 +1129,10 @@ void ShowGiftUpgradedToast(
|
|||
}
|
||||
}
|
||||
|
||||
void SendUpgradeRequest(
|
||||
void SendStarsFormRequest(
|
||||
not_null<Window::SessionController*> controller,
|
||||
Settings::SmallBalanceResult result,
|
||||
uint64 formId,
|
||||
int stars,
|
||||
MTPInputInvoice invoice,
|
||||
Fn<void(Payments::CheckoutResult)> done) {
|
||||
using BalanceResult = Settings::SmallBalanceResult;
|
||||
|
@ -1190,46 +1189,12 @@ void UpgradeGift(
|
|||
return;
|
||||
}
|
||||
using Flag = MTPDinputInvoiceStarGiftUpgrade::Flag;
|
||||
const auto weak = base::make_weak(window);
|
||||
const auto invoice = MTP_inputInvoiceStarGiftUpgrade(
|
||||
MTP_flags(keepDetails ? Flag::f_keep_original_details : Flag()),
|
||||
MTP_int(messageId.bare));
|
||||
session->api().request(MTPpayments_GetPaymentForm(
|
||||
MTP_flags(0),
|
||||
invoice,
|
||||
MTPDataJSON() // theme_params
|
||||
)).done([=](const MTPpayments_PaymentForm &result) {
|
||||
result.match([&](const MTPDpayments_paymentFormStarGift &data) {
|
||||
const auto formId = data.vform_id().v;
|
||||
const auto prices = data.vinvoice().data().vprices().v;
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
done(Payments::CheckoutResult::Failed);
|
||||
return;
|
||||
}
|
||||
const auto ready = [=](Settings::SmallBalanceResult result) {
|
||||
SendUpgradeRequest(
|
||||
strong,
|
||||
result,
|
||||
formId,
|
||||
stars,
|
||||
invoice,
|
||||
done);
|
||||
};
|
||||
Settings::MaybeRequestBalanceIncrease(
|
||||
Main::MakeSessionShow(strong->uiShow(), session),
|
||||
prices.front().data().vamount().v,
|
||||
Settings::SmallBalanceDeepLink{},
|
||||
ready);
|
||||
}, [&](const auto &) {
|
||||
done(Payments::CheckoutResult::Failed);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showToast(error.type());
|
||||
}
|
||||
done(Payments::CheckoutResult::Failed);
|
||||
}).send();
|
||||
RequestStarsFormAndSubmit(
|
||||
window,
|
||||
MTP_inputInvoiceStarGiftUpgrade(
|
||||
MTP_flags(keepDetails ? Flag::f_keep_original_details : Flag()),
|
||||
MTP_int(messageId.bare)),
|
||||
std::move(done));
|
||||
}
|
||||
|
||||
void SoldOutBox(
|
||||
|
@ -2327,4 +2292,41 @@ void AddUniqueCloseButton(not_null<GenericBox*> box) {
|
|||
});
|
||||
}
|
||||
|
||||
void RequestStarsFormAndSubmit(
|
||||
not_null<Window::SessionController*> window,
|
||||
MTPInputInvoice invoice,
|
||||
Fn<void(Payments::CheckoutResult)> done) {
|
||||
const auto weak = base::make_weak(window);
|
||||
window->session().api().request(MTPpayments_GetPaymentForm(
|
||||
MTP_flags(0),
|
||||
invoice,
|
||||
MTPDataJSON() // theme_params
|
||||
)).done([=](const MTPpayments_PaymentForm &result) {
|
||||
result.match([&](const MTPDpayments_paymentFormStarGift &data) {
|
||||
const auto formId = data.vform_id().v;
|
||||
const auto prices = data.vinvoice().data().vprices().v;
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
done(Payments::CheckoutResult::Failed);
|
||||
return;
|
||||
}
|
||||
const auto ready = [=](Settings::SmallBalanceResult result) {
|
||||
SendStarsFormRequest(strong, result, formId, invoice, done);
|
||||
};
|
||||
Settings::MaybeRequestBalanceIncrease(
|
||||
strong->uiShow(),
|
||||
prices.front().data().vamount().v,
|
||||
Settings::SmallBalanceDeepLink{},
|
||||
ready);
|
||||
}, [&](const auto &) {
|
||||
done(Payments::CheckoutResult::Failed);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showToast(error.type());
|
||||
}
|
||||
done(Payments::CheckoutResult::Failed);
|
||||
}).send();
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -13,6 +13,10 @@ struct GiftCode;
|
|||
struct CreditsHistoryEntry;
|
||||
} // namespace Data
|
||||
|
||||
namespace Payments {
|
||||
enum class CheckoutResult;
|
||||
} // namespace Payments
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
@ -60,4 +64,9 @@ void ShowStarGiftUpgradeBox(StarGiftUpgradeArgs &&args);
|
|||
|
||||
void AddUniqueCloseButton(not_null<GenericBox*> box);
|
||||
|
||||
void RequestStarsFormAndSubmit(
|
||||
not_null<Window::SessionController*> window,
|
||||
MTPInputInvoice invoice,
|
||||
Fn<void(Payments::CheckoutResult)> done);
|
||||
|
||||
} // namespace Ui
|
||||
|
|
407
Telegram/SourceFiles/boxes/transfer_gift_box.cpp
Normal file
407
Telegram/SourceFiles/boxes/transfer_gift_box.cpp
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
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 "boxes/transfer_gift_box.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_star_gift.h"
|
||||
#include "data/data_user.h"
|
||||
#include "boxes/filters/edit_filter_chats_list.h" // CreatePe...tionSubtitle.
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/star_gift_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h" // peerListSingleRow.
|
||||
#include "styles/style_dialogs.h" // recentPeersSpecialName.
|
||||
|
||||
namespace {
|
||||
|
||||
struct ExportOption {
|
||||
object_ptr<Ui::RpWidget> content = { nullptr };
|
||||
Fn<bool(int, int, int)> overrideKey;
|
||||
Fn<void()> activate;
|
||||
};
|
||||
|
||||
class Controller final : public ContactsBoxController {
|
||||
public:
|
||||
Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
Fn<void(not_null<PeerData*>)> choose);
|
||||
|
||||
void noSearchSubmit();
|
||||
|
||||
private:
|
||||
void prepareViewHook() override;
|
||||
void setupExportOption();
|
||||
|
||||
bool overrideKeyboardNavigation(
|
||||
int direction,
|
||||
int fromIndex,
|
||||
int toIndex) override;
|
||||
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
const std::shared_ptr<Data::UniqueGift> _gift;
|
||||
const Fn<void(not_null<PeerData*>)> _choose;
|
||||
ExportOption _exportOption;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] ExportOption MakeExportOption(
|
||||
not_null<Window::SessionController*> window,
|
||||
TimeId when) {
|
||||
const auto activate = [=] {
|
||||
const auto now = base::unixtime::now();
|
||||
const auto left = (when > now) ? (when - now) : 0;
|
||||
const auto hours = left ? std::max((left + 1800) / 3600, 1) : 0;
|
||||
window->show(Ui::MakeInformBox({
|
||||
.text = (!hours
|
||||
? tr::lng_gift_transfer_unlocks_update_about()
|
||||
: tr::lng_gift_transfer_unlocks_about(
|
||||
lt_when,
|
||||
((hours >= 24)
|
||||
? tr::lng_gift_transfer_unlocks_when_days(
|
||||
lt_count,
|
||||
rpl::single((hours / 24) * 1.))
|
||||
: tr::lng_gift_transfer_unlocks_when_hours(
|
||||
lt_count,
|
||||
rpl::single(hours * 1.))))),
|
||||
.title = (!hours
|
||||
? tr::lng_gift_transfer_unlocks_update_title()
|
||||
: tr::lng_gift_transfer_unlocks_title()),
|
||||
}));
|
||||
};
|
||||
|
||||
class ExportRow final : public PeerListRow {
|
||||
public:
|
||||
explicit ExportRow(TimeId when)
|
||||
: PeerListRow(Data::FakePeerIdForJustName("ton-export").value) {
|
||||
const auto now = base::unixtime::now();
|
||||
_available = (when <= now);
|
||||
if (const auto left = when - now; left > 0) {
|
||||
const auto hours = std::max((left + 1800) / 3600, 1);
|
||||
const auto days = hours / 24;
|
||||
setCustomStatus(days
|
||||
? tr::lng_gift_transfer_unlocks_days(
|
||||
tr::now,
|
||||
lt_count,
|
||||
days)
|
||||
: tr::lng_gift_transfer_unlocks_hours(
|
||||
tr::now,
|
||||
lt_count,
|
||||
hours));
|
||||
}
|
||||
}
|
||||
|
||||
QString generateName() override {
|
||||
return tr::lng_gift_transfer_via_blockchain(tr::now);
|
||||
}
|
||||
QString generateShortName() override {
|
||||
return generateName();
|
||||
}
|
||||
auto generatePaintUserpicCallback(bool forceRound)
|
||||
-> PaintRoundImageCallback override {
|
||||
return [=](
|
||||
Painter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size) mutable {
|
||||
Ui::EmptyUserpic::PaintCurrency(p, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
|
||||
const style::PeerListItem &computeSt(
|
||||
const style::PeerListItem &st) const {
|
||||
return _available ? st::recentPeersSpecialName : st;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _available = false;
|
||||
|
||||
};
|
||||
|
||||
class ExportController final : public PeerListController {
|
||||
public:
|
||||
ExportController(
|
||||
not_null<Main::Session*> session,
|
||||
TimeId when,
|
||||
Fn<void()> activate)
|
||||
: _session(session)
|
||||
, _when(when)
|
||||
, _activate(std::move(activate)) {
|
||||
}
|
||||
|
||||
void prepare() override {
|
||||
delegate()->peerListAppendRow(
|
||||
std::make_unique<ExportRow>(_when));
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
void loadMoreRows() override {
|
||||
}
|
||||
void rowClicked(not_null<PeerListRow*> row) override {
|
||||
_activate();
|
||||
}
|
||||
Main::Session &session() const override {
|
||||
return *_session;
|
||||
}
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
TimeId _when = 0;
|
||||
Fn<void()> _activate;
|
||||
|
||||
};
|
||||
|
||||
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
|
||||
const auto container = result.data();
|
||||
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto delegate = container->lifetime().make_state<
|
||||
PeerListContentDelegateSimple
|
||||
>();
|
||||
const auto controller = container->lifetime().make_state<
|
||||
ExportController
|
||||
>(&window->session(), when, activate);
|
||||
controller->setStyleOverrides(&st::peerListSingleRow);
|
||||
const auto content = container->add(object_ptr<PeerListContent>(
|
||||
container,
|
||||
controller));
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
Ui::AddSkip(container);
|
||||
container->add(CreatePeerListSectionSubtitle(
|
||||
container,
|
||||
tr::lng_contacts_header()));
|
||||
|
||||
const auto overrideKey = [=](int direction, int from, int to) {
|
||||
if (!content->isVisible()) {
|
||||
return false;
|
||||
} else if (direction > 0 && from < 0 && to >= 0) {
|
||||
if (content->hasSelection()) {
|
||||
const auto was = content->selectedIndex();
|
||||
const auto now = content->selectSkip(1).reallyMovedTo;
|
||||
if (was != now) {
|
||||
return true;
|
||||
}
|
||||
content->clearSelection();
|
||||
} else {
|
||||
content->selectSkip(1);
|
||||
return true;
|
||||
}
|
||||
} else if (direction < 0 && to < 0) {
|
||||
if (!content->hasSelection()) {
|
||||
content->selectLast();
|
||||
} else if (from >= 0 || content->hasSelection()) {
|
||||
content->selectSkip(-1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
.content = std::move(result),
|
||||
.overrideKey = overrideKey,
|
||||
.activate = activate,
|
||||
};
|
||||
}
|
||||
|
||||
Controller::Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
Fn<void(not_null<PeerData*>)> choose)
|
||||
: ContactsBoxController(&window->session())
|
||||
, _gift(std::move(gift))
|
||||
, _choose(std::move(choose)) {
|
||||
if (const auto when = _gift->exportAt) {
|
||||
_exportOption = MakeExportOption(window, when);
|
||||
}
|
||||
if (_exportOption.content) {
|
||||
setStyleOverrides(&st::peerListSmallSkips);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::noSearchSubmit() {
|
||||
if (const auto onstack = _exportOption.activate) {
|
||||
onstack();
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::overrideKeyboardNavigation(
|
||||
int direction,
|
||||
int fromIndex,
|
||||
int toIndex) {
|
||||
return _exportOption.overrideKey
|
||||
&& _exportOption.overrideKey(direction, fromIndex, toIndex);
|
||||
}
|
||||
|
||||
void Controller::prepareViewHook() {
|
||||
delegate()->peerListSetTitle(tr::lng_gift_transfer_title(
|
||||
lt_name,
|
||||
rpl::single(UniqueGiftName(*_gift))));
|
||||
setupExportOption();
|
||||
}
|
||||
|
||||
void Controller::setupExportOption() {
|
||||
delegate()->peerListSetAboveWidget(std::move(_exportOption.content));
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> Controller::createRow(
|
||||
not_null<UserData*> user) {
|
||||
if (user->isSelf()
|
||||
|| user->isBot()
|
||||
|| user->isServiceUser()
|
||||
|| user->isInaccessible()) {
|
||||
return nullptr;
|
||||
}
|
||||
return ContactsBoxController::createRow(user);
|
||||
}
|
||||
|
||||
void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
_choose(row->peer());
|
||||
}
|
||||
|
||||
void TransferGift(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> to,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
MsgId messageId,
|
||||
Fn<void(Payments::CheckoutResult)> done) {
|
||||
Expects(to->isUser());
|
||||
|
||||
const auto session = &window->session();
|
||||
const auto weak = base::make_weak(window);
|
||||
if (gift->starsForTransfer <= 0) {
|
||||
session->api().request(MTPpayments_TransferStarGift(
|
||||
MTP_int(messageId.bare),
|
||||
to->asUser()->inputUser
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
session->api().applyUpdates(result);
|
||||
done(Payments::CheckoutResult::Paid);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->showToast(error.type());
|
||||
}
|
||||
done(Payments::CheckoutResult::Failed);
|
||||
}).send();
|
||||
return;
|
||||
}
|
||||
Ui::RequestStarsFormAndSubmit(
|
||||
window,
|
||||
MTP_inputInvoiceStarGiftTransfer(
|
||||
MTP_int(messageId.bare),
|
||||
to->asUser()->inputUser),
|
||||
std::move(done));
|
||||
}
|
||||
|
||||
void ShowTransferToBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
MsgId msgId) {
|
||||
const auto stars = gift->starsForTransfer;
|
||||
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(tr::lng_gift_transfer_title(
|
||||
lt_name,
|
||||
rpl::single(UniqueGiftName(*gift))));
|
||||
|
||||
auto transfer = (stars > 0)
|
||||
? tr::lng_gift_transfer_button_for(
|
||||
lt_price,
|
||||
tr::lng_action_gift_for_stars(
|
||||
lt_count,
|
||||
rpl::single(stars * 1.)))
|
||||
: tr::lng_gift_transfer_button();
|
||||
|
||||
struct State {
|
||||
bool sent = false;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
auto callback = [=] {
|
||||
if (state->sent) {
|
||||
return;
|
||||
}
|
||||
state->sent = true;
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
const auto done = [=](Payments::CheckoutResult result) {
|
||||
if (result != Payments::CheckoutResult::Paid) {
|
||||
state->sent = false;
|
||||
} else {
|
||||
controller->showPeerHistory(peer);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}
|
||||
};
|
||||
TransferGift(controller, peer, gift, msgId, done);
|
||||
};
|
||||
|
||||
Ui::ConfirmBox(box, {
|
||||
.text = (stars > 0)
|
||||
? tr::lng_gift_transfer_sure_for(
|
||||
lt_name,
|
||||
rpl::single(Ui::Text::Bold(UniqueGiftName(*gift))),
|
||||
lt_recipient,
|
||||
rpl::single(Ui::Text::Bold(peer->shortName())),
|
||||
lt_price,
|
||||
tr::lng_action_gift_for_stars(
|
||||
lt_count,
|
||||
rpl::single(stars * 1.),
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_gift_transfer_sure(
|
||||
lt_name,
|
||||
rpl::single(Ui::Text::Bold(UniqueGiftName(*gift))),
|
||||
lt_recipient,
|
||||
rpl::single(Ui::Text::Bold(peer->shortName())),
|
||||
Ui::Text::WithEntities),
|
||||
.confirmed = std::move(callback),
|
||||
.confirmText = std::move(transfer),
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowTransferGiftBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
MsgId msgId) {
|
||||
auto controller = std::make_unique<Controller>(
|
||||
window,
|
||||
gift,
|
||||
[=](not_null<PeerData*> peer) {
|
||||
ShowTransferToBox(window, peer, gift, msgId);
|
||||
});
|
||||
const auto controllerRaw = controller.get();
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
|
||||
|
||||
box->noSearchSubmits() | rpl::start_with_next([=] {
|
||||
controllerRaw->noSearchSubmit();
|
||||
}, box->lifetime());
|
||||
};
|
||||
window->show(
|
||||
Box<PeerListBox>(std::move(controller), std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
21
Telegram/SourceFiles/boxes/transfer_gift_box.h
Normal file
21
Telegram/SourceFiles/boxes/transfer_gift_box.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
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 Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Data {
|
||||
struct UniqueGift;
|
||||
} // namespace Data
|
||||
|
||||
void ShowTransferGiftBox(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
MsgId msgId);
|
|
@ -84,6 +84,7 @@ struct CreditsHistoryEntry final {
|
|||
bool converted : 1 = false;
|
||||
bool anonymous : 1 = false;
|
||||
bool stargift : 1 = false;
|
||||
bool giftTransferred : 1 = false;
|
||||
bool savedToProfile : 1 = false;
|
||||
bool fromGiftsList : 1 = false;
|
||||
bool soldOutInfo : 1 = false;
|
||||
|
|
|
@ -132,7 +132,6 @@ enum class GiftType : uchar {
|
|||
Premium, // count - months
|
||||
Credits, // count - credits
|
||||
StarGift, // count - stars
|
||||
StarGiftUpgrade,
|
||||
};
|
||||
|
||||
struct GiftCode {
|
||||
|
@ -151,11 +150,14 @@ struct GiftCode {
|
|||
int count = 0;
|
||||
GiftType type = GiftType::Premium;
|
||||
bool viaGiveaway : 1 = false;
|
||||
bool transferred : 1 = false;
|
||||
bool upgradable : 1 = false;
|
||||
bool unclaimed : 1 = false;
|
||||
bool anonymous : 1 = false;
|
||||
bool converted : 1 = false;
|
||||
bool upgraded : 1 = false;
|
||||
bool refunded : 1 = false;
|
||||
bool upgrade : 1 = false;
|
||||
bool saved : 1 = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -38,8 +38,10 @@ struct UniqueGiftOriginalDetails {
|
|||
|
||||
struct UniqueGift {
|
||||
QString title;
|
||||
int number = 0;
|
||||
PeerId ownerId = 0;
|
||||
int number = 0;
|
||||
int starsForTransfer = -1;
|
||||
TimeId exportAt = 0;
|
||||
UniqueGiftModel model;
|
||||
UniqueGiftPattern pattern;
|
||||
UniqueGiftBackdrop backdrop;
|
||||
|
|
|
@ -5463,13 +5463,17 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
const auto isSelf = _from->isSelf();
|
||||
const auto peer = isSelf ? _history->peer : _from;
|
||||
result.links.push_back(peer->createOpenLink());
|
||||
result.text = (isSelf
|
||||
? tr::lng_action_gift_upgraded_mine
|
||||
: tr::lng_action_gift_upgraded)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||
Ui::Text::WithEntities);
|
||||
result.text = (action.is_upgrade()
|
||||
? (isSelf
|
||||
? tr::lng_action_gift_upgraded_mine
|
||||
: tr::lng_action_gift_upgraded)
|
||||
: (isSelf
|
||||
? tr::lng_action_gift_transferred_mine
|
||||
: tr::lng_action_gift_transferred))(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Link(peer->shortName(), 1), // Link 1.
|
||||
Ui::Text::WithEntities);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
@ -5683,7 +5687,10 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
|||
}, [&](const MTPDmessageActionStarGiftUnique &data) {
|
||||
using Fields = Data::GiftCode;
|
||||
auto fields = Fields{
|
||||
.type = Data::GiftType::StarGiftUpgrade,
|
||||
.type = Data::GiftType::StarGift,
|
||||
.transferred = data.is_transferred(),
|
||||
.refunded = data.is_refunded(),
|
||||
.upgrade = data.is_upgrade(),
|
||||
.saved = data.is_saved(),
|
||||
};
|
||||
if (auto gift = Api::FromTL(&history()->session(), data.vgift())) {
|
||||
|
@ -5692,7 +5699,12 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
|||
fields.limitedCount = gift->limitedCount;
|
||||
fields.limitedLeft = gift->limitedLeft;
|
||||
fields.count = gift->stars;
|
||||
fields.unique = gift->unique;
|
||||
fields.unique = std::move(gift->unique);
|
||||
if (const auto unique = fields.unique.get()) {
|
||||
unique->starsForTransfer
|
||||
= data.vtransfer_stars().value_or(-1);
|
||||
unique->exportAt = data.vcan_export_at().value_or_empty();
|
||||
}
|
||||
}
|
||||
_media = std::make_unique<Data::MediaGiftBox>(
|
||||
this,
|
||||
|
|
|
@ -235,7 +235,7 @@ void PremiumGift::draw(
|
|||
|
||||
QString PremiumGift::cornerTagText() {
|
||||
if (_data.unique) {
|
||||
return tr::lng_gift_limited_of_one(tr::now);
|
||||
return tr::lng_gift_collectible_tag(tr::now);
|
||||
} else if (const auto count = _data.limitedCount) {
|
||||
return (count == 1)
|
||||
? tr::lng_gift_limited_of_one(tr::now)
|
||||
|
@ -292,12 +292,11 @@ bool PremiumGift::gift() const {
|
|||
}
|
||||
|
||||
bool PremiumGift::starGift() const {
|
||||
return (_data.type == Data::GiftType::StarGift)
|
||||
|| (_data.type == Data::GiftType::StarGiftUpgrade);
|
||||
return (_data.type == Data::GiftType::StarGift);
|
||||
}
|
||||
|
||||
bool PremiumGift::starGiftUpgrade() const {
|
||||
return (_data.type == Data::GiftType::StarGiftUpgrade);
|
||||
return (_data.type == Data::GiftType::StarGift) && _data.upgrade;
|
||||
}
|
||||
|
||||
bool PremiumGift::creditsPrize() const {
|
||||
|
|
|
@ -400,6 +400,12 @@ auto GenerateUniqueGiftMedia(
|
|||
st));
|
||||
};
|
||||
|
||||
const auto item = parent->data();
|
||||
const auto media = item->media();
|
||||
const auto fields = media ? media->gift() : nullptr;
|
||||
const auto upgrade = fields && fields->upgrade;
|
||||
const auto outgoing = upgrade ? !item->out() : item->out();
|
||||
|
||||
const auto white = QColor(255, 255, 255);
|
||||
const auto sticker = [=] {
|
||||
using Tag = ChatHelpers::StickerLottieSize;
|
||||
|
@ -415,7 +421,7 @@ auto GenerateUniqueGiftMedia(
|
|||
sticker,
|
||||
st::chatUniqueStickerPadding));
|
||||
pushText(
|
||||
Ui::Text::Bold((!parent->data()->out()
|
||||
Ui::Text::Bold((outgoing
|
||||
? tr::lng_action_gift_sent_subtitle
|
||||
: tr::lng_action_gift_got_subtitle)(
|
||||
tr::now,
|
||||
|
@ -430,7 +436,7 @@ auto GenerateUniqueGiftMedia(
|
|||
gift->backdrop.textColor,
|
||||
st::chatUniqueTextPadding);
|
||||
|
||||
const auto withButton = parent->data()->out();
|
||||
const auto withButton = !outgoing;
|
||||
|
||||
auto attributes = std::vector<AttributeTable::Entry>{
|
||||
{ tr::lng_gift_unique_model(tr::now), gift->model.name },
|
||||
|
|
|
@ -63,6 +63,10 @@ QImage IconCurrencyColored(
|
|||
return image;
|
||||
}
|
||||
|
||||
QByteArray CurrencySvgColored(const QColor &c) {
|
||||
return CurrencySvg(c);
|
||||
}
|
||||
|
||||
QImage MenuIconCurrency(const QSize &size) {
|
||||
auto image = QImage(
|
||||
size * style::DevicePixelRatio(),
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Ui::Earn {
|
|||
[[nodiscard]] QImage IconCurrencyColored(
|
||||
const style::font &font,
|
||||
const QColor &c);
|
||||
[[nodiscard]] QByteArray CurrencySvgColored(const QColor &c);
|
||||
|
||||
[[nodiscard]] QImage MenuIconCurrency(const QSize &size);
|
||||
[[nodiscard]] QImage MenuIconCredits();
|
||||
|
|
|
@ -1318,7 +1318,7 @@ void ReceiptCreditsBox(
|
|||
.cost = e.starsUpgradedBySender ? 0 : e.starsToUpgrade,
|
||||
.canAddSender = !e.anonymous,
|
||||
.canAddComment = !e.anonymous && e.hasGiftComment,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
const auto canUpgrade = e.stargiftId
|
||||
|
@ -1369,7 +1369,8 @@ void ReceiptCreditsBox(
|
|||
}
|
||||
});
|
||||
};
|
||||
const auto canToggle = canConvert || couldConvert || nonConvertible;
|
||||
const auto canToggle = (canConvert || couldConvert || nonConvertible)
|
||||
&& !e.giftTransferred;
|
||||
|
||||
AddStarGiftTable(
|
||||
controller,
|
||||
|
@ -1665,7 +1666,9 @@ void StarGiftViewBox(
|
|||
.bareMsgId = uint64(item->id.bare),
|
||||
.barePeerId = item->history()->peer->id.value,
|
||||
.bareGiftStickerId = data.document ? data.document->id : 0,
|
||||
.bareGiftOwnerId = item->history()->session().userPeerId().value,
|
||||
.bareGiftOwnerId = (data.unique
|
||||
? data.unique->ownerId.value
|
||||
: item->history()->session().userPeerId().value),
|
||||
.stargiftId = data.stargiftId,
|
||||
.uniqueGift = data.unique,
|
||||
.peerType = Data::CreditsHistoryEntry::PeerType::Peer,
|
||||
|
@ -1677,6 +1680,7 @@ void StarGiftViewBox(
|
|||
.converted = data.converted,
|
||||
.anonymous = data.anonymous,
|
||||
.stargift = true,
|
||||
.giftTransferred = data.transferred,
|
||||
.savedToProfile = data.saved,
|
||||
.canUpgradeGift = data.upgradable,
|
||||
.hasGiftComment = !data.message.empty(),
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "ui/empty_userpic.h"
|
||||
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/effects/animation_value.h"
|
||||
#include "ui/emoji_config.h"
|
||||
|
@ -17,6 +18,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_widgets.h" // style::IconButton
|
||||
#include "styles/style_info.h" // st::topBarCall
|
||||
|
||||
#include <QtSvg/QSvgRenderer>
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
|
@ -183,6 +186,18 @@ void PaintMyNotesInner(
|
|||
fg);
|
||||
}
|
||||
|
||||
void PaintCurrencyInner(
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int size,
|
||||
const style::color &fg) {
|
||||
auto svg = QSvgRenderer(Ui::Earn::CurrencySvgColored(fg->c));
|
||||
const auto skip = size / 5;
|
||||
svg.render(&p, QRect(x, y, size, size).marginsRemoved(
|
||||
{ skip, skip, skip, skip }));
|
||||
}
|
||||
|
||||
void PaintExternalMessagesInner(
|
||||
QPainter &p,
|
||||
int x,
|
||||
|
@ -507,6 +522,45 @@ QImage EmptyUserpic::GenerateMyNotes(int size) {
|
|||
});
|
||||
}
|
||||
|
||||
void EmptyUserpic::PaintCurrency(
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size) {
|
||||
auto bg = QLinearGradient(x, y, x, y + size);
|
||||
bg.setStops({
|
||||
{ 0., st::historyPeerSavedMessagesBg->c },
|
||||
{ 1., st::historyPeerSavedMessagesBg2->c }
|
||||
});
|
||||
const auto &fg = st::historyPeerUserpicFg;
|
||||
PaintCurrency(p, x, y, outerWidth, size, QBrush(bg), fg);
|
||||
}
|
||||
|
||||
void EmptyUserpic::PaintCurrency(
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size,
|
||||
QBrush bg,
|
||||
const style::color &fg) {
|
||||
x = style::RightToLeft() ? (outerWidth - x - size) : x;
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setBrush(bg);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawEllipse(x, y, size, size);
|
||||
|
||||
PaintCurrencyInner(p, x, y, size, fg);
|
||||
}
|
||||
|
||||
QImage EmptyUserpic::GenerateCurrency(int size) {
|
||||
return Generate(size, [&](QPainter &p) {
|
||||
PaintCurrency(p, 0, 0, size, size);
|
||||
});
|
||||
}
|
||||
|
||||
std::pair<uint64, uint64> EmptyUserpic::uniqueKey() const {
|
||||
const auto first = (uint64(0xFFFFFFFFU) << 32)
|
||||
| anim::getPremultiplied(_colors.color1->c);
|
||||
|
|
|
@ -113,6 +113,22 @@ public:
|
|||
const style::color &fg);
|
||||
[[nodiscard]] static QImage GenerateMyNotes(int size);
|
||||
|
||||
static void PaintCurrency(
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size);
|
||||
static void PaintCurrency(
|
||||
QPainter &p,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size,
|
||||
QBrush bg,
|
||||
const style::color &fg);
|
||||
[[nodiscard]] static QImage GenerateCurrency(int size);
|
||||
|
||||
~EmptyUserpic();
|
||||
|
||||
private:
|
||||
|
|
Loading…
Add table
Reference in a new issue