mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Allow placing bid with message / anonymous.
This commit is contained in:
@@ -4018,6 +4018,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_auction_bought#one" = "{count} {emoji} item bought {arrow}";
|
||||
"lng_auction_bought#other" = "{count} {emoji} items bought {arrow}";
|
||||
"lng_auction_join_button" = "Join Auction";
|
||||
"lng_auction_join_bid" = "Place a Bid";
|
||||
"lng_auction_join_time_left" = "{time} left";
|
||||
"lng_auction_join_time_medium" = "{hours} h {minutes} m";
|
||||
"lng_auction_join_time_small" = "{minutes} m";
|
||||
@@ -4070,6 +4071,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_auction_bought_bid" = "Accepted Bid";
|
||||
"lng_auction_bought_round" = "Round #{n}";
|
||||
"lng_auction_bought_top" = "Top {n}";
|
||||
"lng_auction_change_title" = "Change Recipient";
|
||||
"lng_auction_change_button" = "Change";
|
||||
"lng_auction_change_already" = "You've already placed a bid on this gift for {name}.";
|
||||
"lng_auction_change_to" = "Do you want to raise your bid and change the recipient to {name}?";
|
||||
"lng_auction_change_already_me" = "You've already placed a bid on this gift for yourself.";
|
||||
"lng_auction_change_to_me" = "Do you want to raise your bid and change the recipient to yourself?";
|
||||
|
||||
"lng_accounts_limit_title" = "Limit Reached";
|
||||
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/star_gift_auction_box.h"
|
||||
|
||||
#include "api/api_text_entities.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/send_credits_box.h" // CreditsEmojiSmall
|
||||
#include "boxes/star_gift_box.h"
|
||||
@@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "settings/settings_credits_graphics.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/controls/button_labels.h"
|
||||
#include "ui/controls/feature_list.h"
|
||||
#include "ui/controls/table_rows.h"
|
||||
@@ -71,7 +73,7 @@ struct BidRowData {
|
||||
BidType type = BidType::Setting;
|
||||
|
||||
friend inline bool operator==(
|
||||
const BidRowData &,
|
||||
const BidRowData &,
|
||||
const BidRowData &) = default;
|
||||
};
|
||||
|
||||
@@ -207,8 +209,8 @@ struct BidRowData {
|
||||
crl::on_main(was, [=] { delete was; });
|
||||
}
|
||||
state->userpic = std::make_unique<UserpicButton>(
|
||||
raw,
|
||||
state->user,
|
||||
raw,
|
||||
state->user,
|
||||
st::auctionBidUserpic);
|
||||
state->userpic->show();
|
||||
state->userpic->moveToLeft(userpicLeft, 0);
|
||||
@@ -241,31 +243,34 @@ struct BidRowData {
|
||||
return result;
|
||||
}
|
||||
|
||||
struct AuctionBidBoxArgs {
|
||||
not_null<PeerData*> peer;
|
||||
std::shared_ptr<ChatHelpers::Show> show;
|
||||
rpl::producer<Data::GiftAuctionState> state;
|
||||
};
|
||||
|
||||
void PlaceAuctionBid(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> to,
|
||||
int64 amount,
|
||||
const Data::GiftAuctionState &state,
|
||||
std::unique_ptr<Info::PeerGifts::GiftSendDetails> details,
|
||||
Fn<void(Payments::CheckoutResult)> done) {
|
||||
auto paymentDone = [=](
|
||||
Payments::CheckoutResult result,
|
||||
const MTPUpdates *updates) {
|
||||
done(result);
|
||||
};
|
||||
const auto passDetails = (details || !state.my.bid);
|
||||
const auto hideName = passDetails && details && details->anonymous;
|
||||
const auto text = details ? details->text : TextWithEntities();
|
||||
|
||||
using Flag = MTPDinputInvoiceStarGiftAuctionBid::Flag;
|
||||
const auto invoice = MTP_inputInvoiceStarGiftAuctionBid(
|
||||
MTP_flags((state.my.bid ? Flag::f_update_bid : Flag())
|
||||
| (state.my.bid ? Flag() : Flag::f_peer)),
|
||||
state.my.bid ? MTP_inputPeerEmpty() : to->input,
|
||||
| (passDetails ? Flag::f_peer : Flag())
|
||||
| (passDetails ? Flag::f_message : Flag())
|
||||
| (hideName ? Flag::f_hide_name : Flag())),
|
||||
passDetails ? to->input : MTP_inputPeerEmpty(),
|
||||
MTP_long(state.gift->id),
|
||||
MTP_long(amount),
|
||||
MTPTextWithEntities());
|
||||
MTP_textWithEntities(
|
||||
MTP_string(text.text),
|
||||
Api::EntitiesToMTP(&to->session(), text.entities)));
|
||||
RequestOurForm(show, invoice, [=](
|
||||
uint64 formId,
|
||||
CreditsAmount price,
|
||||
@@ -334,7 +339,7 @@ object_ptr<RpWidget> MakeAuctionInfoBlocks(
|
||||
{
|
||||
.title = std::move(bidTitle),
|
||||
.subtext = tr::lng_auction_bid_minimal(
|
||||
lt_count,
|
||||
lt_count,
|
||||
std::move(minimal)),
|
||||
},
|
||||
{
|
||||
@@ -349,9 +354,9 @@ object_ptr<RpWidget> MakeAuctionInfoBlocks(
|
||||
}
|
||||
|
||||
void AddBidPlaces(
|
||||
not_null<GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
rpl::producer<Data::GiftAuctionState> value,
|
||||
not_null<GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
rpl::producer<Data::GiftAuctionState> value,
|
||||
rpl::producer<int> chosen) {
|
||||
struct My {
|
||||
BidType type;
|
||||
@@ -408,8 +413,8 @@ void AddBidPlaces(
|
||||
|
||||
for (auto i = begin(levels), e = end(levels); i != e; ++i) {
|
||||
if (i->amount < chosen
|
||||
|| (!setting
|
||||
&& i->amount == chosen
|
||||
|| (!setting
|
||||
&& i->amount == chosen
|
||||
&& i->date > value.my.date)) {
|
||||
top.push_back({ show->session().user(), chosen });
|
||||
for (auto j = i; j != e; ++j) {
|
||||
@@ -446,7 +451,7 @@ void AddBidPlaces(
|
||||
return BidRowData{ user, stars, place, my.type };
|
||||
});
|
||||
box->addRow(MakeBidRow(box, show, std::move(bid)));
|
||||
|
||||
|
||||
AddSubsectionTitle(
|
||||
box->verticalLayout(),
|
||||
tr::lng_auction_bid_winners_title(),
|
||||
@@ -468,6 +473,7 @@ void AddBidPlaces(
|
||||
void AuctionBidBox(not_null<GenericBox*> box, AuctionBidBoxArgs &&args) {
|
||||
const auto weak = base::make_weak(box);
|
||||
|
||||
using namespace Info::PeerGifts;
|
||||
struct State {
|
||||
State(rpl::producer<Data::GiftAuctionState> value)
|
||||
: value(std::move(value)) {
|
||||
@@ -492,6 +498,9 @@ void AuctionBidBox(not_null<GenericBox*> box, AuctionBidBoxArgs &&args) {
|
||||
state->chosen = chosen;
|
||||
|
||||
const auto show = args.show;
|
||||
const auto details = args.details
|
||||
? *args.details
|
||||
: std::optional<GiftSendDetails>();
|
||||
const auto save = [=, peer = args.peer](int amount) {
|
||||
const auto ¤t = state->value.current();
|
||||
if (amount > current.my.bid) {
|
||||
@@ -513,7 +522,10 @@ void AuctionBidBox(not_null<GenericBox*> box, AuctionBidBoxArgs &&args) {
|
||||
});
|
||||
}
|
||||
};
|
||||
PlaceAuctionBid(show, peer, amount, now, done);
|
||||
auto owned = details
|
||||
? std::make_unique<GiftSendDetails>(*details)
|
||||
: nullptr;
|
||||
PlaceAuctionBid(show, peer, amount, now, std::move(owned), done);
|
||||
}
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
@@ -562,7 +574,7 @@ void AuctionBidBox(not_null<GenericBox*> box, AuctionBidBoxArgs &&args) {
|
||||
box->addRow(
|
||||
MakeAuctionInfoBlocks(box, &show->session(), state->value.value()),
|
||||
st::boxRowPadding + QMargins(0, skip / 2, 0, skip));
|
||||
|
||||
|
||||
AddBidPlaces(box, show, state->value.value(), state->chosen.value());
|
||||
|
||||
AddSkip(content);
|
||||
@@ -592,16 +604,11 @@ void AuctionBidBox(not_null<GenericBox*> box, AuctionBidBoxArgs &&args) {
|
||||
}) | rpl::flatten_latest());
|
||||
|
||||
AddStarSelectBalance(
|
||||
box,
|
||||
&show->session(),
|
||||
box,
|
||||
&show->session(),
|
||||
show->session().credits().balanceValue());
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<BoxContent> MakeAuctionBidBox(
|
||||
AuctionBidBoxArgs &&args) {
|
||||
return Box(AuctionBidBox, std::move(args));
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<RpWidget> MakeAveragePriceValue(
|
||||
not_null<TableLayout*> table,
|
||||
std::shared_ptr<TableRowTooltipData> tooltip,
|
||||
@@ -740,8 +747,8 @@ void AuctionAboutBox(
|
||||
});
|
||||
box->addRow(
|
||||
Calls::Group::MakeRoundActiveLogo(
|
||||
box,
|
||||
st::auctionAboutLogo,
|
||||
box,
|
||||
st::auctionAboutLogo,
|
||||
st::auctionAboutLogoPadding),
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
|
||||
@@ -765,8 +772,8 @@ void AuctionAboutBox(
|
||||
{
|
||||
st::menuIconRatingGifts,
|
||||
tr::lng_auction_about_top_title(
|
||||
tr::now,
|
||||
lt_count,
|
||||
tr::now,
|
||||
lt_count,
|
||||
giftsPerRound),
|
||||
tr::lng_auction_about_top_about(
|
||||
tr::now,
|
||||
@@ -774,14 +781,14 @@ void AuctionAboutBox(
|
||||
giftsPerRound,
|
||||
lt_rounds,
|
||||
tr::lng_auction_about_top_rounds(
|
||||
tr::now,
|
||||
lt_count,
|
||||
tr::now,
|
||||
lt_count,
|
||||
rounds,
|
||||
tr::rich),
|
||||
lt_bidders,
|
||||
tr::lng_auction_about_top_bidders(
|
||||
tr::now,
|
||||
lt_count,
|
||||
tr::now,
|
||||
lt_count,
|
||||
giftsPerRound,
|
||||
tr::rich),
|
||||
tr::rich),
|
||||
@@ -811,7 +818,7 @@ void AuctionAboutBox(
|
||||
}
|
||||
});
|
||||
box->addButton(
|
||||
rpl::single(QString()),
|
||||
rpl::single(QString()),
|
||||
understood ? [=] { understood(close); } : close
|
||||
)->setText(rpl::single(Text::IconEmoji(
|
||||
&st::infoStarsUnderstood
|
||||
@@ -819,8 +826,8 @@ void AuctionAboutBox(
|
||||
}
|
||||
|
||||
void AuctionGotGiftsBox(
|
||||
not_null<GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
const Data::StarGift &gift,
|
||||
std::vector<Data::GiftAcquired> list) {
|
||||
Expects(!list.empty());
|
||||
@@ -830,7 +837,7 @@ void AuctionGotGiftsBox(
|
||||
tr::lng_auction_bought_title(lt_count, rpl::single(count * 1.)));
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setMaxHeight(st::boxWideWidth * 2);
|
||||
|
||||
|
||||
auto helper = Text::CustomEmojiHelper(Core::TextContext({
|
||||
.session = &show->session(),
|
||||
}));
|
||||
@@ -858,7 +865,7 @@ void AuctionGotGiftsBox(
|
||||
|
||||
// Title "Round #n"
|
||||
addFullWidth(tr::lng_auction_bought_round(
|
||||
lt_n,
|
||||
lt_n,
|
||||
rpl::single(tr::marked(QString::number(entry.round))),
|
||||
tr::bold
|
||||
) | rpl::map([=](const TextWithEntities &text) {
|
||||
@@ -888,9 +895,9 @@ void AuctionGotGiftsBox(
|
||||
helper.paletteDependent(
|
||||
Text::CustomEmojiTextBadge(
|
||||
tr::lng_auction_bought_top(
|
||||
tr::now,
|
||||
lt_n,
|
||||
QString::number(entry.position)).toUpper(),
|
||||
tr::now,
|
||||
lt_n,
|
||||
QString::number(entry.position)).toUpper(),
|
||||
st::defaultTableSmallButton)));
|
||||
AddTableRow(
|
||||
table,
|
||||
@@ -907,7 +914,7 @@ void AuctionGotGiftsBox(
|
||||
|
||||
void AuctionInfoBox(
|
||||
not_null<GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
rpl::producer<Data::GiftAuctionState> value) {
|
||||
using namespace Info::PeerGifts;
|
||||
@@ -924,6 +931,7 @@ void AuctionInfoBox(
|
||||
std::vector<Data::GiftAcquired> acquired;
|
||||
bool acquiredRequested = false;
|
||||
};
|
||||
const auto show = window->uiShow();
|
||||
const auto state = box->lifetime().make_state<State>(&show->session());
|
||||
state->value = std::move(value);
|
||||
state->minutesLeft = MinutesLeftTillValue(
|
||||
@@ -1027,15 +1035,16 @@ void AuctionInfoBox(
|
||||
return false;
|
||||
} else if (state->acquired.size() == value.my.gotCount) {
|
||||
show->show(Box(
|
||||
AuctionGotGiftsBox,
|
||||
show,
|
||||
AuctionGotGiftsBox,
|
||||
show,
|
||||
gift,
|
||||
state->acquired));
|
||||
} else if (!state->acquiredRequested) {
|
||||
state->acquiredRequested = true;
|
||||
show->session().giftAuctions().requestAcquired(
|
||||
value.gift->id,
|
||||
crl::guard(box, [=](std::vector<Data::GiftAcquired> result) {
|
||||
crl::guard(box, [=](
|
||||
std::vector<Data::GiftAcquired> result) {
|
||||
state->acquiredRequested = false;
|
||||
state->acquired = std::move(result);
|
||||
if (!state->acquired.empty()) {
|
||||
@@ -1057,61 +1066,23 @@ void AuctionInfoBox(
|
||||
box->closeBox();
|
||||
return;
|
||||
}
|
||||
const auto bidBox = show->show(MakeAuctionBidBox({
|
||||
.peer = peer,
|
||||
.show = show,
|
||||
.state = state->value.value(),
|
||||
}));
|
||||
bidBox->boxClosing(
|
||||
const auto sendBox = show->show(Box(
|
||||
SendGiftBox,
|
||||
window,
|
||||
peer,
|
||||
nullptr,
|
||||
GiftTypeStars{ .info = *state->value.current().gift },
|
||||
state->value.value()));
|
||||
sendBox->boxClosing(
|
||||
) | rpl::start_with_next([=] {
|
||||
box->closeBox();
|
||||
}, box->lifetime());
|
||||
});
|
||||
|
||||
auto buttonTitle = rpl::combine(
|
||||
state->value.value(),
|
||||
state->minutesLeft.value()
|
||||
) | rpl::map([=](const Data::GiftAuctionState &state, int minutes) {
|
||||
return (state.finished() || minutes <= 0)
|
||||
? tr::lng_box_ok(tr::marked)
|
||||
: tr::lng_auction_join_button(tr::marked);
|
||||
}) | rpl::flatten_latest();
|
||||
|
||||
auto buttonSubtitle = rpl::combine(
|
||||
state->value.value(),
|
||||
state->minutesLeft.value()
|
||||
) | rpl::map([=](const Data::GiftAuctionState &state, int minutes) {
|
||||
if (state.finished() || minutes <= 0) {
|
||||
return rpl::single(TextWithEntities());
|
||||
}
|
||||
const auto hours = (minutes / 60);
|
||||
minutes -= (hours * 60);
|
||||
|
||||
auto value = [](int count) {
|
||||
return rpl::single(tr::marked(QString::number(count)));
|
||||
};
|
||||
return tr::lng_auction_join_time_left(
|
||||
lt_time,
|
||||
(hours
|
||||
? tr::lng_auction_join_time_medium(
|
||||
lt_hours,
|
||||
value(hours),
|
||||
lt_minutes,
|
||||
value(minutes),
|
||||
tr::marked)
|
||||
: tr::lng_auction_join_time_small(
|
||||
lt_minutes,
|
||||
value(minutes),
|
||||
tr::marked)),
|
||||
tr::marked);
|
||||
}) | rpl::flatten_latest();
|
||||
|
||||
SetButtonTwoLabels(
|
||||
SetAuctionButtonCountdownText(
|
||||
button,
|
||||
std::move(buttonTitle),
|
||||
std::move(buttonSubtitle),
|
||||
st::resaleButtonTitle,
|
||||
st::resaleButtonSubtitle);
|
||||
AuctionButtonCountdownType::Join,
|
||||
state->value.value());
|
||||
|
||||
rpl::combine(
|
||||
state->value.value(),
|
||||
@@ -1135,7 +1106,7 @@ void AuctionInfoBox(
|
||||
}
|
||||
|
||||
base::weak_qptr<BoxContent> ChooseAndShowAuctionBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<rpl::variable<Data::GiftAuctionState>> state,
|
||||
Fn<void()> boxClosed) {
|
||||
@@ -1143,28 +1114,65 @@ base::weak_qptr<BoxContent> ChooseAndShowAuctionBox(
|
||||
const auto &now = state->current();
|
||||
const auto finished = now.finished()
|
||||
|| (now.endDate <= base::unixtime::now());
|
||||
const auto showBidBox = now.my.bid && !finished;
|
||||
const auto showBidBox = now.my.bid
|
||||
&& !finished
|
||||
&& (!now.my.to || now.my.to == peer);
|
||||
const auto showChangeRecipient = !showBidBox && now.my.bid && !finished;
|
||||
const auto showInfoBox = !showBidBox
|
||||
&& !showChangeRecipient
|
||||
&& (local->readPref<bool>(kAuctionAboutShownPref) || finished);
|
||||
auto box = base::weak_qptr<BoxContent>();
|
||||
if (showBidBox) {
|
||||
box = show->show(MakeAuctionBidBox({
|
||||
box = window->show(MakeAuctionBidBox({
|
||||
.peer = peer,
|
||||
.show = show,
|
||||
.show = window->uiShow(),
|
||||
.state = state->value(),
|
||||
}));
|
||||
} else if (showChangeRecipient) {
|
||||
const auto change = [=](Fn<void()> close) {
|
||||
const auto sendBox = window->show(Box(
|
||||
SendGiftBox,
|
||||
window,
|
||||
peer,
|
||||
nullptr,
|
||||
Info::PeerGifts::GiftTypeStars{
|
||||
.info = *now.gift,
|
||||
},
|
||||
state->value()));
|
||||
sendBox->boxClosing(
|
||||
) | rpl::start_with_next(close, sendBox->lifetime());
|
||||
};
|
||||
const auto text = (now.my.to->isSelf()
|
||||
? tr::lng_auction_change_already_me(tr::now, tr::rich)
|
||||
: tr::lng_auction_change_already(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::bold(now.my.to->name()),
|
||||
tr::rich)).append(' ').append(peer->isSelf()
|
||||
? tr::lng_auction_change_to_me(tr::now, tr::rich)
|
||||
: tr::lng_auction_change_to(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::bold(peer->name()),
|
||||
tr::rich));
|
||||
box = window->show(MakeConfirmBox({
|
||||
.text = text,
|
||||
.confirmed = change,
|
||||
.confirmText = tr::lng_auction_change_button(),
|
||||
.title = tr::lng_auction_change_title(),
|
||||
}));
|
||||
} else if (showInfoBox) {
|
||||
box = show->show(Box(
|
||||
box = window->show(Box(
|
||||
AuctionInfoBox,
|
||||
show,
|
||||
window,
|
||||
peer,
|
||||
state->value()));
|
||||
} else {
|
||||
local->writePref<bool>(kAuctionAboutShownPref, true);
|
||||
const auto understood = [=](Fn<void()> close) {
|
||||
ChooseAndShowAuctionBox(show, peer, state, close);
|
||||
ChooseAndShowAuctionBox(window, peer, state, close);
|
||||
};
|
||||
box = show->show(Box(
|
||||
box = window->show(Box(
|
||||
AuctionAboutBox,
|
||||
now.totalRounds,
|
||||
now.gift->auctionGiftsPerRound,
|
||||
@@ -1205,17 +1213,16 @@ rpl::lifetime ShowStarGiftAuction(
|
||||
state->value = std::move(value);
|
||||
if (initial) {
|
||||
if (const auto strong = weak.get()) {
|
||||
const auto show = strong->uiShow();
|
||||
const auto to = peer
|
||||
? peer
|
||||
: already
|
||||
? already
|
||||
: show->session().user();
|
||||
? peer
|
||||
: already
|
||||
? already
|
||||
: strong->session().user();
|
||||
state->box = ChooseAndShowAuctionBox(
|
||||
show,
|
||||
to,
|
||||
strong,
|
||||
to,
|
||||
std::shared_ptr<rpl::variable<Data::GiftAuctionState>>(
|
||||
state,
|
||||
state,
|
||||
&state->value),
|
||||
boxClosed);
|
||||
} else {
|
||||
@@ -1231,4 +1238,69 @@ rpl::lifetime ShowStarGiftAuction(
|
||||
return result;
|
||||
}
|
||||
|
||||
object_ptr<BoxContent> MakeAuctionBidBox(AuctionBidBoxArgs &&args) {
|
||||
return Box(AuctionBidBox, std::move(args));
|
||||
}
|
||||
|
||||
void SetAuctionButtonCountdownText(
|
||||
not_null<RoundButton*> button,
|
||||
AuctionButtonCountdownType type,
|
||||
rpl::producer<Data::GiftAuctionState> value) {
|
||||
struct State {
|
||||
rpl::variable<Data::GiftAuctionState> value;
|
||||
rpl::variable<int> minutesLeft;
|
||||
};
|
||||
const auto state = button->lifetime().make_state<State>();
|
||||
state->value = std::move(value);
|
||||
state->minutesLeft = MinutesLeftTillValue(
|
||||
state->value.current().endDate);
|
||||
|
||||
auto buttonTitle = rpl::combine(
|
||||
state->value.value(),
|
||||
state->minutesLeft.value()
|
||||
) | rpl::map([=](const Data::GiftAuctionState &state, int minutes) {
|
||||
return (state.finished() || minutes <= 0)
|
||||
? tr::lng_box_ok(tr::marked)
|
||||
: (type == AuctionButtonCountdownType::Join)
|
||||
? tr::lng_auction_join_button(tr::marked)
|
||||
: tr::lng_auction_join_bid(tr::marked);
|
||||
}) | rpl::flatten_latest();
|
||||
|
||||
auto buttonSubtitle = rpl::combine(
|
||||
state->value.value(),
|
||||
state->minutesLeft.value()
|
||||
) | rpl::map([=](const Data::GiftAuctionState &state, int minutes) {
|
||||
if (state.finished() || minutes <= 0) {
|
||||
return rpl::single(TextWithEntities());
|
||||
}
|
||||
const auto hours = (minutes / 60);
|
||||
minutes -= (hours * 60);
|
||||
|
||||
auto value = [](int count) {
|
||||
return rpl::single(tr::marked(QString::number(count)));
|
||||
};
|
||||
return tr::lng_auction_join_time_left(
|
||||
lt_time,
|
||||
(hours
|
||||
? tr::lng_auction_join_time_medium(
|
||||
lt_hours,
|
||||
value(hours),
|
||||
lt_minutes,
|
||||
value(minutes),
|
||||
tr::marked)
|
||||
: tr::lng_auction_join_time_small(
|
||||
lt_minutes,
|
||||
value(minutes),
|
||||
tr::marked)),
|
||||
tr::marked);
|
||||
}) | rpl::flatten_latest();
|
||||
|
||||
SetButtonTwoLabels(
|
||||
button,
|
||||
std::move(buttonTitle),
|
||||
std::move(buttonSubtitle),
|
||||
st::resaleButtonTitle,
|
||||
st::resaleButtonSubtitle);
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -7,12 +7,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace ChatHelpers {
|
||||
class Show;
|
||||
} // namespace ChatHelpers
|
||||
|
||||
namespace Data {
|
||||
struct GiftAuctionState;
|
||||
} // namespace Data
|
||||
|
||||
namespace Info::PeerGifts {
|
||||
struct GiftSendDetails;
|
||||
} // namespace Info::PeerGifts
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class BoxContent;
|
||||
class RoundButton;
|
||||
|
||||
[[nodiscard]] rpl::lifetime ShowStarGiftAuction(
|
||||
not_null<Window::SessionController*> controller,
|
||||
PeerData *peer,
|
||||
@@ -20,4 +35,22 @@ namespace Ui {
|
||||
Fn<void()> finishRequesting,
|
||||
Fn<void()> boxClosed);
|
||||
|
||||
struct AuctionBidBoxArgs {
|
||||
not_null<PeerData*> peer;
|
||||
std::shared_ptr<ChatHelpers::Show> show;
|
||||
rpl::producer<Data::GiftAuctionState> state;
|
||||
std::unique_ptr<Info::PeerGifts::GiftSendDetails> details;
|
||||
};
|
||||
[[nodiscard]] object_ptr<BoxContent> MakeAuctionBidBox(
|
||||
AuctionBidBoxArgs &&args);
|
||||
|
||||
enum class AuctionButtonCountdownType {
|
||||
Join,
|
||||
Place,
|
||||
};
|
||||
void SetAuctionButtonCountdownText(
|
||||
not_null<RoundButton*> button,
|
||||
AuctionButtonCountdownType type,
|
||||
rpl::producer<Data::GiftAuctionState> value);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/tabbed_selector.h"
|
||||
#include "core/application.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/components/gift_auctions.h"
|
||||
#include "data/components/promo_suggestions.h"
|
||||
#include "data/data_birthday.h"
|
||||
#include "data/data_changes.h"
|
||||
@@ -159,15 +160,6 @@ struct PremiumGiftsDescriptor {
|
||||
std::shared_ptr<Api::PremiumGiftCodeOptions> api;
|
||||
};
|
||||
|
||||
struct GiftDetails {
|
||||
GiftDescriptor descriptor;
|
||||
TextWithEntities text;
|
||||
uint64 randomId = 0;
|
||||
bool anonymous = false;
|
||||
bool upgraded = false;
|
||||
bool byStars = false;
|
||||
};
|
||||
|
||||
struct SessionResalePrices {
|
||||
explicit SessionResalePrices(not_null<Main::Session*> session)
|
||||
: api(std::make_unique<Api::PremiumGiftCodeOptions>(session->user())) {
|
||||
@@ -291,14 +283,14 @@ public:
|
||||
PreviewWrap(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<PeerData*> recipient,
|
||||
rpl::producer<GiftDetails> details);
|
||||
rpl::producer<GiftSendDetails> details);
|
||||
~PreviewWrap();
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void resizeTo(int width);
|
||||
void prepare(rpl::producer<GiftDetails> details);
|
||||
void prepare(rpl::producer<GiftSendDetails> details);
|
||||
|
||||
const not_null<History*> _history;
|
||||
const not_null<PeerData*> _recipient;
|
||||
@@ -437,7 +429,7 @@ auto GenerateGiftMedia(
|
||||
not_null<Element*> parent,
|
||||
Element *replacing,
|
||||
not_null<PeerData*> recipient,
|
||||
const GiftDetails &data)
|
||||
const GiftSendDetails &data)
|
||||
-> Fn<void(
|
||||
not_null<MediaGeneric*>,
|
||||
Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
||||
@@ -571,7 +563,7 @@ auto GenerateGiftMedia(
|
||||
PreviewWrap::PreviewWrap(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<PeerData*> recipient,
|
||||
rpl::producer<GiftDetails> details)
|
||||
rpl::producer<GiftSendDetails> details)
|
||||
: RpWidget(parent)
|
||||
, _history(recipient->owner().history(recipient->session().userPeerId()))
|
||||
, _recipient(recipient)
|
||||
@@ -603,7 +595,7 @@ PreviewWrap::PreviewWrap(
|
||||
void ShowSentToast(
|
||||
not_null<Window::SessionController*> window,
|
||||
const GiftDescriptor &descriptor,
|
||||
const GiftDetails &details) {
|
||||
const GiftSendDetails &details) {
|
||||
const auto &st = st::historyPremiumToast;
|
||||
const auto skip = st.padding.top();
|
||||
const auto size = st.style.font->height * 2;
|
||||
@@ -686,8 +678,8 @@ PreviewWrap::~PreviewWrap() {
|
||||
_item = {};
|
||||
}
|
||||
|
||||
void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
|
||||
std::move(details) | rpl::start_with_next([=](GiftDetails details) {
|
||||
void PreviewWrap::prepare(rpl::producer<GiftSendDetails> details) {
|
||||
std::move(details) | rpl::start_with_next([=](GiftSendDetails details) {
|
||||
const auto &descriptor = details.descriptor;
|
||||
const auto cost = v::match(descriptor, [&](GiftTypePremium data) {
|
||||
const auto stars = (details.byStars && data.stars)
|
||||
@@ -1234,7 +1226,7 @@ void SendGift(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Api::PremiumGiftCodeOptions> api,
|
||||
const GiftDetails &details,
|
||||
const GiftSendDetails &details,
|
||||
Fn<void(Payments::CheckoutResult)> done) {
|
||||
const auto processNonPanelPaymentFormFactory
|
||||
= Payments::ProcessNonPanelPaymentFormFactory(window, done);
|
||||
@@ -1618,292 +1610,6 @@ void CheckMaybeGiftLocked(
|
||||
})).send();
|
||||
}
|
||||
|
||||
void SendGiftBox(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Api::PremiumGiftCodeOptions> api,
|
||||
const GiftDescriptor &descriptor) {
|
||||
const auto stars = std::get_if<GiftTypeStars>(&descriptor);
|
||||
const auto limited = stars
|
||||
&& (stars->info.limitedCount > stars->info.limitedLeft)
|
||||
&& (stars->info.limitedLeft > 0);
|
||||
const auto costToUpgrade = stars ? stars->info.starsToUpgrade : 0;
|
||||
const auto user = peer->asUser();
|
||||
const auto disallowed = user
|
||||
? user->disallowedGiftTypes()
|
||||
: Api::DisallowedGiftTypes();
|
||||
const auto disallowLimited = !peer->isSelf()
|
||||
&& (disallowed & Api::DisallowedGiftType::Limited);
|
||||
box->setStyle(limited ? st::giftLimitedBox : st::giftBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setTitle(tr::lng_gift_send_title());
|
||||
box->addTopButton(st::boxTitleClose, [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
|
||||
const auto session = &window->session();
|
||||
|
||||
struct State {
|
||||
rpl::variable<GiftDetails> details;
|
||||
rpl::variable<bool> messageAllowed;
|
||||
std::shared_ptr<Data::DocumentMedia> media;
|
||||
bool submitting = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
state->details = GiftDetails{
|
||||
.descriptor = descriptor,
|
||||
.randomId = base::RandomValue<uint64>(),
|
||||
.upgraded = disallowLimited && (costToUpgrade > 0),
|
||||
};
|
||||
peer->updateFull();
|
||||
state->messageAllowed = peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::StarsPerMessage
|
||||
) | rpl::map([=] {
|
||||
return peer->starsPerMessageChecked() == 0;
|
||||
});
|
||||
|
||||
auto cost = state->details.value(
|
||||
) | rpl::map([](const GiftDetails &details) {
|
||||
return v::match(details.descriptor, [&](const GiftTypePremium &data) {
|
||||
const auto stars = (details.byStars && data.stars)
|
||||
? data.stars
|
||||
: (data.currency == kCreditsCurrency)
|
||||
? data.cost
|
||||
: 0;
|
||||
if (stars) {
|
||||
return CreditsEmojiSmall().append(
|
||||
Lang::FormatCountDecimal(std::abs(stars)));
|
||||
}
|
||||
return TextWithEntities{
|
||||
FillAmountAndCurrency(data.cost, data.currency),
|
||||
};
|
||||
}, [&](const GiftTypeStars &data) {
|
||||
const auto amount = std::abs(data.info.stars)
|
||||
+ (details.upgraded ? data.info.starsToUpgrade : 0);
|
||||
return CreditsEmojiSmall().append(
|
||||
Lang::FormatCountDecimal(amount));
|
||||
});
|
||||
});
|
||||
|
||||
const auto document = LookupGiftSticker(session, descriptor);
|
||||
if ((state->media = document ? document->createMediaView() : nullptr)) {
|
||||
state->media->checkStickerLarge();
|
||||
}
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
container->add(object_ptr<PreviewWrap>(
|
||||
container,
|
||||
peer,
|
||||
state->details.value()));
|
||||
|
||||
const auto messageWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
messageWrap->toggleOn(state->messageAllowed.value());
|
||||
messageWrap->finishAnimating();
|
||||
const auto messageInner = messageWrap->entity();
|
||||
const auto limit = StarGiftMessageLimit(session);
|
||||
const auto text = AddPartInput(
|
||||
window,
|
||||
messageInner,
|
||||
box->getDelegate()->outerContainer(),
|
||||
tr::lng_gift_send_message(),
|
||||
QString(),
|
||||
limit);
|
||||
text->changes() | rpl::start_with_next([=] {
|
||||
auto now = state->details.current();
|
||||
auto textWithTags = text->getTextWithAppliedMarkdown();
|
||||
now.text = TextWithEntities{
|
||||
std::move(textWithTags.text),
|
||||
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
|
||||
};
|
||||
state->details = std::move(now);
|
||||
}, text->lifetime());
|
||||
|
||||
box->setFocusCallback([=] {
|
||||
text->setFocusFast();
|
||||
});
|
||||
|
||||
const auto allow = [=](not_null<DocumentData*> emoji) {
|
||||
return true;
|
||||
};
|
||||
InitMessageFieldHandlers({
|
||||
.session = session,
|
||||
.show = window->uiShow(),
|
||||
.field = text,
|
||||
.customEmojiPaused = [=] {
|
||||
using namespace Window;
|
||||
return window->isGifPausedAtLeastFor(GifPauseReason::Layer);
|
||||
},
|
||||
.allowPremiumEmoji = allow,
|
||||
.allowMarkdownTags = {
|
||||
InputField::kTagBold,
|
||||
InputField::kTagItalic,
|
||||
InputField::kTagUnderline,
|
||||
InputField::kTagStrikeOut,
|
||||
InputField::kTagSpoiler,
|
||||
}
|
||||
});
|
||||
Emoji::SuggestionsController::Init(
|
||||
box->getDelegate()->outerContainer(),
|
||||
text,
|
||||
session,
|
||||
{ .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow });
|
||||
if (stars) {
|
||||
if (costToUpgrade > 0 && !peer->isSelf() && !disallowLimited) {
|
||||
const auto id = stars->info.id;
|
||||
const auto showing = std::make_shared<bool>();
|
||||
AddDivider(container);
|
||||
AddSkip(container);
|
||||
AddUpgradeButton(container, costToUpgrade, peer, [=](bool on) {
|
||||
auto now = state->details.current();
|
||||
now.upgraded = on;
|
||||
state->details = std::move(now);
|
||||
}, [=] {
|
||||
if (*showing) {
|
||||
return;
|
||||
}
|
||||
*showing = true;
|
||||
ShowStarGiftUpgradeBox({
|
||||
.controller = window,
|
||||
.stargiftId = id,
|
||||
.ready = [=](bool) { *showing = false; },
|
||||
.peer = peer,
|
||||
.cost = int(costToUpgrade),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
AddDivider(container);
|
||||
}
|
||||
AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<SettingsButton>(
|
||||
container,
|
||||
tr::lng_gift_send_anonymous(),
|
||||
st::settingsButtonNoIcon)
|
||||
)->toggleOn(rpl::single(peer->isSelf()))->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
auto now = state->details.current();
|
||||
now.anonymous = toggled;
|
||||
state->details = std::move(now);
|
||||
}, container->lifetime());
|
||||
AddSkip(container);
|
||||
}
|
||||
v::match(descriptor, [&](const GiftTypePremium &data) {
|
||||
AddDividerText(messageInner, tr::lng_gift_send_premium_about(
|
||||
lt_user,
|
||||
rpl::single(peer->shortName())));
|
||||
|
||||
if (const auto byStars = data.stars) {
|
||||
const auto star = Ui::Text::IconEmoji(&st::starIconEmojiColored);
|
||||
AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<SettingsButton>(
|
||||
container,
|
||||
tr::lng_gift_send_pay_with_stars(
|
||||
lt_amount,
|
||||
rpl::single(base::duplicate(star).append(Lang::FormatCountDecimal(byStars))),
|
||||
Ui::Text::WithEntities),
|
||||
st::settingsButtonNoIcon)
|
||||
)->toggleOn(rpl::single(false))->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
auto now = state->details.current();
|
||||
now.byStars = toggled;
|
||||
state->details = std::move(now);
|
||||
}, container->lifetime());
|
||||
AddSkip(container);
|
||||
|
||||
const auto balance = AddDividerText(
|
||||
container,
|
||||
tr::lng_gift_send_stars_balance(
|
||||
lt_amount,
|
||||
peer->session().credits().balanceValue(
|
||||
) | rpl::map([=](CreditsAmount amount) {
|
||||
return base::duplicate(star).append(
|
||||
Lang::FormatCreditsAmountDecimal(amount));
|
||||
}),
|
||||
lt_link,
|
||||
tr::lng_gift_send_stars_balance_link(
|
||||
) | Ui::Text::ToLink(),
|
||||
Ui::Text::WithEntities));
|
||||
struct State {
|
||||
Settings::BuyStarsHandler buyStars;
|
||||
rpl::variable<bool> loading;
|
||||
};
|
||||
const auto state = balance->lifetime().make_state<State>();
|
||||
state->loading = state->buyStars.loadingValue();
|
||||
balance->setClickHandlerFilter([=](const auto &...) {
|
||||
if (!state->loading.current()) {
|
||||
state->buyStars.handler(window->uiShow())();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}, [&](const GiftTypeStars &) {
|
||||
AddDividerText(container, peer->isSelf()
|
||||
? tr::lng_gift_send_anonymous_self()
|
||||
: peer->isBroadcast()
|
||||
? tr::lng_gift_send_anonymous_about_channel()
|
||||
: rpl::conditional(
|
||||
state->messageAllowed.value(),
|
||||
tr::lng_gift_send_anonymous_about(
|
||||
lt_user,
|
||||
rpl::single(peer->shortName()),
|
||||
lt_recipient,
|
||||
rpl::single(peer->shortName())),
|
||||
tr::lng_gift_send_anonymous_about_paid(
|
||||
lt_user,
|
||||
rpl::single(peer->shortName()),
|
||||
lt_recipient,
|
||||
rpl::single(peer->shortName()))));
|
||||
});
|
||||
|
||||
const auto button = box->addButton(rpl::single(QString()), [=] {
|
||||
if (state->submitting) {
|
||||
return;
|
||||
}
|
||||
state->submitting = true;
|
||||
auto details = state->details.current();
|
||||
if (!state->messageAllowed.current()) {
|
||||
details.text = {};
|
||||
}
|
||||
const auto copy = state->media; // Let media outlive the box.
|
||||
const auto weak = base::make_weak(box);
|
||||
const auto done = [=](Payments::CheckoutResult result) {
|
||||
if (result == Payments::CheckoutResult::Paid) {
|
||||
if (details.byStars
|
||||
|| v::is<GiftTypeStars>(details.descriptor)) {
|
||||
window->session().credits().load(true);
|
||||
}
|
||||
const auto another = copy; // Let media outlive the box.
|
||||
window->showPeerHistory(peer);
|
||||
ShowSentToast(window, details.descriptor, details);
|
||||
}
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
SendGift(window, peer, api, details, done);
|
||||
});
|
||||
if (limited) {
|
||||
AddSoldLeftSlider(button, *stars);
|
||||
}
|
||||
SetButtonMarkedLabel(
|
||||
button,
|
||||
(peer->isSelf()
|
||||
? tr::lng_gift_send_button_self
|
||||
: tr::lng_gift_send_button)(
|
||||
lt_cost,
|
||||
std::move(cost),
|
||||
Text::WithEntities),
|
||||
session,
|
||||
st::creditsBoxButtonLabel,
|
||||
&st::giftBox.button.textFg);
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
@@ -2012,7 +1718,8 @@ void SendGiftBox(
|
||||
window,
|
||||
peer,
|
||||
state->api,
|
||||
descriptor));
|
||||
descriptor,
|
||||
nullptr));
|
||||
});
|
||||
const auto unique = star ? star->info.unique : nullptr;
|
||||
const auto premiumNeeded = star && star->info.requirePremium;
|
||||
@@ -4443,12 +4150,6 @@ void UpgradeBox(
|
||||
AddUniqueCloseButton(box, {});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ShowStarGiftUpgradeBox(StarGiftUpgradeArgs &&args) {
|
||||
const auto weak = base::make_weak(args.controller);
|
||||
const auto session = &args.peer->session();
|
||||
@@ -4742,4 +4443,316 @@ object_ptr<RpWidget> MakeGiftsSendList(
|
||||
std::move(loadMore));
|
||||
}
|
||||
|
||||
void SendGiftBox(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Api::PremiumGiftCodeOptions> api,
|
||||
const GiftDescriptor &descriptor,
|
||||
rpl::producer<Data::GiftAuctionState> auctionState) {
|
||||
const auto stars = std::get_if<GiftTypeStars>(&descriptor);
|
||||
const auto limited = stars
|
||||
&& (stars->info.limitedCount > stars->info.limitedLeft)
|
||||
&& (stars->info.limitedLeft > 0);
|
||||
const auto costToUpgrade = stars ? stars->info.starsToUpgrade : 0;
|
||||
const auto user = peer->asUser();
|
||||
const auto disallowed = user
|
||||
? user->disallowedGiftTypes()
|
||||
: Api::DisallowedGiftTypes();
|
||||
const auto disallowLimited = !peer->isSelf()
|
||||
&& (disallowed & Api::DisallowedGiftType::Limited);
|
||||
box->setStyle(limited ? st::giftLimitedBox : st::giftBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setTitle(tr::lng_gift_send_title());
|
||||
box->addTopButton(st::boxTitleClose, [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
|
||||
const auto session = &window->session();
|
||||
|
||||
struct State {
|
||||
rpl::variable<GiftSendDetails> details;
|
||||
rpl::variable<bool> messageAllowed;
|
||||
std::shared_ptr<Data::DocumentMedia> media;
|
||||
rpl::variable<Data::GiftAuctionState> auction;
|
||||
bool submitting = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
if (auctionState) {
|
||||
state->auction = std::move(auctionState);
|
||||
}
|
||||
state->details = GiftSendDetails{
|
||||
.descriptor = descriptor,
|
||||
.randomId = base::RandomValue<uint64>(),
|
||||
.upgraded = disallowLimited && (costToUpgrade > 0),
|
||||
};
|
||||
peer->updateFull();
|
||||
state->messageAllowed = peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::StarsPerMessage
|
||||
) | rpl::map([=] {
|
||||
return peer->starsPerMessageChecked() == 0;
|
||||
});
|
||||
|
||||
auto cost = state->details.value(
|
||||
) | rpl::map([](const GiftSendDetails &details) {
|
||||
return v::match(details.descriptor, [&](const GiftTypePremium &data) {
|
||||
const auto stars = (details.byStars && data.stars)
|
||||
? data.stars
|
||||
: (data.currency == kCreditsCurrency)
|
||||
? data.cost
|
||||
: 0;
|
||||
if (stars) {
|
||||
return CreditsEmojiSmall().append(
|
||||
Lang::FormatCountDecimal(std::abs(stars)));
|
||||
}
|
||||
return TextWithEntities{
|
||||
FillAmountAndCurrency(data.cost, data.currency),
|
||||
};
|
||||
}, [&](const GiftTypeStars &data) {
|
||||
const auto amount = std::abs(data.info.stars)
|
||||
+ (details.upgraded ? data.info.starsToUpgrade : 0);
|
||||
return CreditsEmojiSmall().append(
|
||||
Lang::FormatCountDecimal(amount));
|
||||
});
|
||||
});
|
||||
|
||||
const auto document = LookupGiftSticker(session, descriptor);
|
||||
if ((state->media = document ? document->createMediaView() : nullptr)) {
|
||||
state->media->checkStickerLarge();
|
||||
}
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
container->add(object_ptr<PreviewWrap>(
|
||||
container,
|
||||
peer,
|
||||
state->details.value()));
|
||||
|
||||
const auto messageWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
messageWrap->toggleOn(state->messageAllowed.value());
|
||||
messageWrap->finishAnimating();
|
||||
const auto messageInner = messageWrap->entity();
|
||||
const auto limit = StarGiftMessageLimit(session);
|
||||
const auto text = AddPartInput(
|
||||
window,
|
||||
messageInner,
|
||||
box->getDelegate()->outerContainer(),
|
||||
tr::lng_gift_send_message(),
|
||||
QString(),
|
||||
limit);
|
||||
text->changes() | rpl::start_with_next([=] {
|
||||
auto now = state->details.current();
|
||||
auto textWithTags = text->getTextWithAppliedMarkdown();
|
||||
now.text = TextWithEntities{
|
||||
std::move(textWithTags.text),
|
||||
TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
|
||||
};
|
||||
state->details = std::move(now);
|
||||
}, text->lifetime());
|
||||
|
||||
box->setFocusCallback([=] {
|
||||
text->setFocusFast();
|
||||
});
|
||||
|
||||
const auto allow = [=](not_null<DocumentData*> emoji) {
|
||||
return true;
|
||||
};
|
||||
InitMessageFieldHandlers({
|
||||
.session = session,
|
||||
.show = window->uiShow(),
|
||||
.field = text,
|
||||
.customEmojiPaused = [=] {
|
||||
using namespace Window;
|
||||
return window->isGifPausedAtLeastFor(GifPauseReason::Layer);
|
||||
},
|
||||
.allowPremiumEmoji = allow,
|
||||
.allowMarkdownTags = {
|
||||
InputField::kTagBold,
|
||||
InputField::kTagItalic,
|
||||
InputField::kTagUnderline,
|
||||
InputField::kTagStrikeOut,
|
||||
InputField::kTagSpoiler,
|
||||
}
|
||||
});
|
||||
Emoji::SuggestionsController::Init(
|
||||
box->getDelegate()->outerContainer(),
|
||||
text,
|
||||
session,
|
||||
{ .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow });
|
||||
if (stars) {
|
||||
if (costToUpgrade > 0 && !peer->isSelf() && !disallowLimited) {
|
||||
const auto id = stars->info.id;
|
||||
const auto showing = std::make_shared<bool>();
|
||||
AddDivider(container);
|
||||
AddSkip(container);
|
||||
AddUpgradeButton(container, costToUpgrade, peer, [=](bool on) {
|
||||
auto now = state->details.current();
|
||||
now.upgraded = on;
|
||||
state->details = std::move(now);
|
||||
}, [=] {
|
||||
if (*showing) {
|
||||
return;
|
||||
}
|
||||
*showing = true;
|
||||
ShowStarGiftUpgradeBox({
|
||||
.controller = window,
|
||||
.stargiftId = id,
|
||||
.ready = [=](bool) { *showing = false; },
|
||||
.peer = peer,
|
||||
.cost = int(costToUpgrade),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
AddDivider(container);
|
||||
}
|
||||
AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<SettingsButton>(
|
||||
container,
|
||||
tr::lng_gift_send_anonymous(),
|
||||
st::settingsButtonNoIcon)
|
||||
)->toggleOn(rpl::single(peer->isSelf()))->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
auto now = state->details.current();
|
||||
now.anonymous = toggled;
|
||||
state->details = std::move(now);
|
||||
}, container->lifetime());
|
||||
AddSkip(container);
|
||||
}
|
||||
v::match(descriptor, [&](const GiftTypePremium &data) {
|
||||
AddDividerText(messageInner, tr::lng_gift_send_premium_about(
|
||||
lt_user,
|
||||
rpl::single(peer->shortName())));
|
||||
|
||||
if (const auto byStars = data.stars) {
|
||||
const auto star = Ui::Text::IconEmoji(&st::starIconEmojiColored);
|
||||
AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<SettingsButton>(
|
||||
container,
|
||||
tr::lng_gift_send_pay_with_stars(
|
||||
lt_amount,
|
||||
rpl::single(base::duplicate(star).append(Lang::FormatCountDecimal(byStars))),
|
||||
Ui::Text::WithEntities),
|
||||
st::settingsButtonNoIcon)
|
||||
)->toggleOn(rpl::single(false))->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
auto now = state->details.current();
|
||||
now.byStars = toggled;
|
||||
state->details = std::move(now);
|
||||
}, container->lifetime());
|
||||
AddSkip(container);
|
||||
|
||||
const auto balance = AddDividerText(
|
||||
container,
|
||||
tr::lng_gift_send_stars_balance(
|
||||
lt_amount,
|
||||
peer->session().credits().balanceValue(
|
||||
) | rpl::map([=](CreditsAmount amount) {
|
||||
return base::duplicate(star).append(
|
||||
Lang::FormatCreditsAmountDecimal(amount));
|
||||
}),
|
||||
lt_link,
|
||||
tr::lng_gift_send_stars_balance_link(
|
||||
) | Ui::Text::ToLink(),
|
||||
Ui::Text::WithEntities));
|
||||
struct State {
|
||||
Settings::BuyStarsHandler buyStars;
|
||||
rpl::variable<bool> loading;
|
||||
};
|
||||
const auto state = balance->lifetime().make_state<State>();
|
||||
state->loading = state->buyStars.loadingValue();
|
||||
balance->setClickHandlerFilter([=](const auto &...) {
|
||||
if (!state->loading.current()) {
|
||||
state->buyStars.handler(window->uiShow())();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}, [&](const GiftTypeStars &) {
|
||||
AddDividerText(container, peer->isSelf()
|
||||
? tr::lng_gift_send_anonymous_self()
|
||||
: peer->isBroadcast()
|
||||
? tr::lng_gift_send_anonymous_about_channel()
|
||||
: rpl::conditional(
|
||||
state->messageAllowed.value(),
|
||||
tr::lng_gift_send_anonymous_about(
|
||||
lt_user,
|
||||
rpl::single(peer->shortName()),
|
||||
lt_recipient,
|
||||
rpl::single(peer->shortName())),
|
||||
tr::lng_gift_send_anonymous_about_paid(
|
||||
lt_user,
|
||||
rpl::single(peer->shortName()),
|
||||
lt_recipient,
|
||||
rpl::single(peer->shortName()))));
|
||||
});
|
||||
|
||||
const auto button = box->addButton(rpl::single(QString()), [=] {
|
||||
if (state->submitting) {
|
||||
return;
|
||||
}
|
||||
state->submitting = true;
|
||||
auto details = state->details.current();
|
||||
if (!state->messageAllowed.current()) {
|
||||
details.text = {};
|
||||
}
|
||||
const auto stars = std::get_if<GiftTypeStars>(&details.descriptor);
|
||||
if (stars && stars->info.auction()) {
|
||||
const auto bidBox = window->show(MakeAuctionBidBox({
|
||||
.peer = peer,
|
||||
.show = window->uiShow(),
|
||||
.state = state->auction.value(),
|
||||
.details = std::make_unique<GiftSendDetails>(details),
|
||||
}));
|
||||
bidBox->boxClosing(
|
||||
) | rpl::start_with_next([=] {
|
||||
box->closeBox();
|
||||
}, box->lifetime());
|
||||
return;
|
||||
}
|
||||
const auto copy = state->media; // Let media outlive the box.
|
||||
const auto weak = base::make_weak(box);
|
||||
const auto done = [=](Payments::CheckoutResult result) {
|
||||
if (result == Payments::CheckoutResult::Paid) {
|
||||
if (details.byStars
|
||||
|| v::is<GiftTypeStars>(details.descriptor)) {
|
||||
window->session().credits().load(true);
|
||||
}
|
||||
const auto another = copy; // Let media outlive the box.
|
||||
window->showPeerHistory(peer);
|
||||
ShowSentToast(window, details.descriptor, details);
|
||||
}
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
SendGift(window, peer, api, details, done);
|
||||
});
|
||||
if (limited) {
|
||||
AddSoldLeftSlider(button, *stars);
|
||||
}
|
||||
if (stars && stars->info.auction()) {
|
||||
SetAuctionButtonCountdownText(
|
||||
button,
|
||||
AuctionButtonCountdownType::Place,
|
||||
state->auction.value());
|
||||
} else {
|
||||
SetButtonMarkedLabel(
|
||||
button,
|
||||
(peer->isSelf()
|
||||
? tr::lng_gift_send_button_self
|
||||
: tr::lng_gift_send_button)(
|
||||
lt_cost,
|
||||
std::move(cost),
|
||||
Text::WithEntities),
|
||||
session,
|
||||
st::creditsBoxButtonLabel,
|
||||
&st::giftBox.button.textFg);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -22,6 +22,7 @@ struct UniqueGift;
|
||||
struct GiftCode;
|
||||
struct CreditsHistoryEntry;
|
||||
class SavedStarGiftId;
|
||||
struct GiftAuctionState;
|
||||
} // namespace Data
|
||||
|
||||
namespace Info::PeerGifts {
|
||||
@@ -162,4 +163,12 @@ struct GiftsDescriptor {
|
||||
rpl::producer<GiftsDescriptor> gifts,
|
||||
Fn<void()> loadMore);
|
||||
|
||||
void SendGiftBox(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Api::PremiumGiftCodeOptions> api,
|
||||
const Info::PeerGifts::GiftDescriptor &descriptor,
|
||||
rpl::producer<Data::GiftAuctionState> auctionState);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -105,6 +105,15 @@ struct GiftDescriptor : std::variant<GiftTypePremium, GiftTypeStars> {
|
||||
const GiftDescriptor&) = default;
|
||||
};
|
||||
|
||||
struct GiftSendDetails {
|
||||
GiftDescriptor descriptor;
|
||||
TextWithEntities text;
|
||||
uint64 randomId = 0;
|
||||
bool anonymous = false;
|
||||
bool upgraded = false;
|
||||
bool byStars = false;
|
||||
};
|
||||
|
||||
struct GiftBadge {
|
||||
QString text;
|
||||
QColor bg1;
|
||||
|
||||
@@ -3747,6 +3747,7 @@ void Account::writePrefs() {
|
||||
}
|
||||
|
||||
void Account::readPrefs() {
|
||||
return; AssertIsDebug();
|
||||
FileReadDescriptor prefs;
|
||||
if (!ReadEncryptedFile(prefs, _prefsKey, _basePath, _localKey)) {
|
||||
ClearKey(_prefsKey, _basePath);
|
||||
|
||||
Reference in New Issue
Block a user