mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Support giveaway message layout.
This commit is contained in:
parent
d5147c9d28
commit
b08869abdb
14 changed files with 659 additions and 251 deletions
|
@ -2091,7 +2091,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_prizes_participants_new#one" = "All users who joined the channel below after this date:";
|
||||
"lng_prizes_participants_new#other" = "All users who joined the channels below after this date:";
|
||||
"lng_prizes_date" = "Winners Selection Date";
|
||||
"lng_prizes_how_works" = "How does it work?";
|
||||
"lng_prizes_how_works" = "Learn more";
|
||||
"lng_prizes_how_text#one" = "This giveaway is sponsored by the admins of {channel}, who aquired **{count} Telegram Premium** subscription for {duration} for its followers.";
|
||||
"lng_prizes_how_text#other" = "This giveaway is sponsored by the admins of {channel}, who aquired **{count} Telegram Premium** subscriptions for {duration} for its followers.";
|
||||
"lng_prizes_how_when_all_of_one#one" = "On {date}, Telegram will automatically select {count} random subscribers of {channel}.";
|
||||
|
|
|
@ -96,6 +96,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_chat.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "payments/payments_form.h"
|
||||
#include "base/random.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtCore/QMimeData>
|
||||
|
@ -449,6 +453,43 @@ HistoryInner::HistoryInner(
|
|||
_migrated->translateTo(_history->translatedTo());
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (const auto channel = _history->peer->asBroadcast()) {
|
||||
if (channel->amCreator()) {
|
||||
const auto weak = base::make_weak(_controller);
|
||||
channel->session().api().request(MTPpayments_GetPremiumGiftCodeOptions(
|
||||
MTP_flags(MTPpayments_GetPremiumGiftCodeOptions::Flag::f_boost_peer),
|
||||
channel->input
|
||||
)).done(crl::guard(weak, [=](const MTPVector<MTPPremiumGiftCodeOption> &result) {
|
||||
if (result.v.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto &data = result.v.front().data();
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
Payments::CheckoutProcess::Start(
|
||||
Payments::InvoicePremiumGiftCode{
|
||||
.purpose = Payments::InvoicePremiumGiftCodeGiveaway{
|
||||
.boostPeer = channel,
|
||||
//.additionalChannels = ,
|
||||
.untilDate = (base::unixtime::now() + 300),
|
||||
.onlyNewSubscribers = true,
|
||||
},
|
||||
.randomId = randomId,
|
||||
.currency = qs(data.vcurrency()),
|
||||
.amount = data.vamount().v,
|
||||
.storeProduct = qs(data.vstore_product().value_or_empty()),
|
||||
.storeQuantity = data.vstore_quantity().value_or_empty(),
|
||||
.users = data.vusers().v,
|
||||
.months = data.vmonths().v,
|
||||
},
|
||||
crl::guard(weak, [=](auto) { weak->window().activate(); }));
|
||||
})).fail(crl::guard(weak, [=](const MTP::Error &error) {
|
||||
weak.get()->showToast(error.type());
|
||||
})).send();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Window::ChatThemeValueFromPeer(
|
||||
controller,
|
||||
_peer
|
||||
|
|
|
@ -338,7 +338,6 @@ void UnreadBar::paint(
|
|||
text);
|
||||
}
|
||||
|
||||
|
||||
void DateBadge::init(const QString &date) {
|
||||
text = date;
|
||||
width = st::msgServiceFont->width(text);
|
||||
|
@ -361,6 +360,81 @@ void DateBadge::paint(
|
|||
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
|
||||
}
|
||||
|
||||
void ServicePreMessage::init(TextWithEntities string) {
|
||||
text = Ui::Text::String(
|
||||
st::serviceTextStyle,
|
||||
string,
|
||||
kMarkupTextOptions,
|
||||
st::msgMinWidth);
|
||||
}
|
||||
|
||||
int ServicePreMessage::resizeToWidth(int newWidth, bool chatWide) {
|
||||
width = newWidth;
|
||||
if (chatWide) {
|
||||
accumulate_min(
|
||||
width,
|
||||
st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
||||
}
|
||||
auto contentWidth = width;
|
||||
contentWidth -= st::msgServiceMargin.left() + st::msgServiceMargin.left(); // two small margins
|
||||
if (contentWidth < st::msgServicePadding.left() + st::msgServicePadding.right() + 1) {
|
||||
contentWidth = st::msgServicePadding.left() + st::msgServicePadding.right() + 1;
|
||||
}
|
||||
|
||||
auto maxWidth = text.maxWidth()
|
||||
+ st::msgServicePadding.left()
|
||||
+ st::msgServicePadding.right();
|
||||
auto minHeight = text.minHeight();
|
||||
|
||||
auto nwidth = qMax(contentWidth
|
||||
- st::msgServicePadding.left()
|
||||
- st::msgServicePadding.right(), 0);
|
||||
height = (contentWidth >= maxWidth)
|
||||
? minHeight
|
||||
: text.countHeight(nwidth);
|
||||
height += st::msgServicePadding.top()
|
||||
+ st::msgServicePadding.bottom()
|
||||
+ st::msgServiceMargin.top()
|
||||
+ st::msgServiceMargin.bottom();
|
||||
return height;
|
||||
}
|
||||
|
||||
void ServicePreMessage::paint(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
QRect g,
|
||||
bool chatWide) const {
|
||||
const auto top = g.top() - height - st::msgMargin.top();
|
||||
p.translate(0, top);
|
||||
|
||||
const auto rect = QRect(0, 0, width, height)
|
||||
- st::msgServiceMargin;
|
||||
const auto trect = rect - st::msgServicePadding;
|
||||
|
||||
ServiceMessagePainter::PaintComplexBubble(
|
||||
p,
|
||||
context.st,
|
||||
rect.left(),
|
||||
rect.width(),
|
||||
text,
|
||||
trect);
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(context.st->msgServiceFg());
|
||||
p.setFont(st::msgServiceFont);
|
||||
text.draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
.availableWidth = trect.width(),
|
||||
.align = style::al_top,
|
||||
.palette = &context.st->serviceTextPalette(),
|
||||
.now = context.now,
|
||||
//.selection = context.selection,
|
||||
.fullWidthSelection = false,
|
||||
});
|
||||
|
||||
p.translate(0, -top);
|
||||
}
|
||||
|
||||
void FakeBotAboutTop::init() {
|
||||
if (!text.isEmpty()) {
|
||||
return;
|
||||
|
@ -971,7 +1045,9 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
|
|||
|| !item->from()->isChannel());
|
||||
};
|
||||
const auto item = data();
|
||||
if (!Has<DateBadge>() && !Has<UnreadBar>()) {
|
||||
if (!Has<DateBadge>()
|
||||
&& !Has<UnreadBar>()
|
||||
&& !Has<ServicePreMessage>()) {
|
||||
const auto prev = previous->data();
|
||||
const auto previousMarkup = prev->inlineReplyMarkup();
|
||||
const auto possible = (std::abs(prev->date() - item->date())
|
||||
|
@ -1183,6 +1259,18 @@ void Element::setDisplayDate(bool displayDate) {
|
|||
}
|
||||
}
|
||||
|
||||
void Element::setServicePreMessage(TextWithEntities text) {
|
||||
if (!text.empty()) {
|
||||
AddComponents(ServicePreMessage::Bit());
|
||||
const auto service = Get<ServicePreMessage>();
|
||||
service->init(std::move(text));
|
||||
setPendingResize();
|
||||
} else if (Has<ServicePreMessage>()) {
|
||||
RemoveComponents(ServicePreMessage::Bit());
|
||||
setPendingResize();
|
||||
}
|
||||
}
|
||||
|
||||
void Element::setAttachToNext(bool attachToNext, Element *next) {
|
||||
Expects(next || !attachToNext);
|
||||
|
||||
|
|
|
@ -234,6 +234,26 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
|
|||
|
||||
};
|
||||
|
||||
// Any HistoryView::Element can have this Component for
|
||||
// displaying some text in layout of a service message above the message.
|
||||
struct ServicePreMessage
|
||||
: public RuntimeComponent<ServicePreMessage, Element> {
|
||||
void init(TextWithEntities string);
|
||||
|
||||
int resizeToWidth(int newWidth, bool chatWide);
|
||||
|
||||
void paint(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
QRect g,
|
||||
bool chatWide) const;
|
||||
|
||||
Ui::Text::String text;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
};
|
||||
|
||||
struct FakeBotAboutTop : public RuntimeComponent<FakeBotAboutTop, Element> {
|
||||
void init();
|
||||
|
||||
|
@ -339,6 +359,7 @@ public:
|
|||
|
||||
// For blocks context this should be called only from recountDisplayDate().
|
||||
void setDisplayDate(bool displayDate);
|
||||
void setServicePreMessage(TextWithEntities text);
|
||||
|
||||
bool computeIsAttachToPrevious(not_null<Element*> previous);
|
||||
|
||||
|
|
|
@ -874,6 +874,9 @@ int Message::marginTop() const {
|
|||
if (const auto bar = Get<UnreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
result += service->height;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -911,6 +914,10 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
}
|
||||
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
service->paint(p, context, g, delegate()->elementIsChatWide());
|
||||
}
|
||||
|
||||
if (isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
@ -3043,6 +3050,9 @@ bool Message::hasFromName() const {
|
|||
if (hasOutLayout() && !item->from()->isChannel()) {
|
||||
return false;
|
||||
} else if (!peer->isUser()) {
|
||||
if (const auto media = this->media()) {
|
||||
return !media->hideFromName();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
|
||||
|
@ -3703,6 +3713,10 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
|
||||
auto newHeight = minHeight();
|
||||
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
service->resizeToWidth(newWidth, delegate()->elementIsChatWide());
|
||||
}
|
||||
|
||||
const auto item = data();
|
||||
const auto botTop = item->isFakeBotAbout()
|
||||
? Get<FakeBotAboutTop>()
|
||||
|
|
|
@ -83,12 +83,23 @@ inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
|
|||
: QString());
|
||||
}
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr MakeWebPageButtonClickHandler(
|
||||
[[nodiscard]] ClickHandlerPtr MakeMediaButtonClickHandler(
|
||||
not_null<Data::Media*> media) {
|
||||
Expects(media->webpage() != nullptr);
|
||||
if (const auto giveaway = media->giveaway()) {
|
||||
return std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
const auto webpage = media->webpage();
|
||||
Assert(webpage != nullptr);
|
||||
|
||||
const auto url = media->webpage()->url;
|
||||
const auto type = media->webpage()->type;
|
||||
const auto url = webpage->url;
|
||||
const auto type = webpage->type;
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
|
@ -105,6 +116,15 @@ inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
|
|||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] QString MakeMediaButtonText(not_null<Data::Media*> media) {
|
||||
if (const auto giveaway = media->giveaway()) {
|
||||
return Ui::Text::Upper(tr::lng_prizes_how_works(tr::now));
|
||||
}
|
||||
const auto webpage = media->webpage();
|
||||
Assert(webpage != nullptr);
|
||||
return WebPageToPhrase(webpage);
|
||||
}
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr SponsoredLink(
|
||||
not_null<HistoryMessageSponsored*> sponsored) {
|
||||
if (!sponsored->externalLink.isEmpty()) {
|
||||
|
@ -170,7 +190,7 @@ struct ViewButton::Inner {
|
|||
bool ViewButton::MediaHasViewButton(not_null<Data::Media*> media) {
|
||||
return media->webpage()
|
||||
? MediaHasViewButton(media->webpage())
|
||||
: false;
|
||||
: (media->giveaway() != nullptr);
|
||||
}
|
||||
|
||||
bool ViewButton::MediaHasViewButton(
|
||||
|
@ -209,10 +229,10 @@ ViewButton::Inner::Inner(
|
|||
not_null<Data::Media*> media,
|
||||
Fn<void()> updateCallback)
|
||||
: margins(st::historyViewButtonMargins)
|
||||
, link(MakeWebPageButtonClickHandler(media))
|
||||
, link(MakeMediaButtonClickHandler(media))
|
||||
, updateCallback(std::move(updateCallback))
|
||||
, belowInfo(false)
|
||||
, text(st::historyViewButtonTextStyle, WebPageToPhrase(media->webpage())) {
|
||||
, text(st::historyViewButtonTextStyle, MakeMediaButtonText(media)) {
|
||||
}
|
||||
|
||||
void ViewButton::Inner::updateMask(int height) {
|
||||
|
|
|
@ -8,74 +8,91 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/media/history_view_giveaway.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "data/data_session.h"
|
||||
#include "dialogs/ui/dialogs_stories_content.h"
|
||||
#include "dialogs/ui/dialogs_stories_list.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/round_rect.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
void TextRows::add(Ui::Text::String text, int skipTop) {
|
||||
constexpr auto kChannelBgAlpha = 32;
|
||||
|
||||
[[nodiscard]] QSize CountOptimalTextSize(
|
||||
const Ui::Text::String &text,
|
||||
int minWidth,
|
||||
int maxWidth) {
|
||||
if (text.maxWidth() <= maxWidth) {
|
||||
return { text.maxWidth(), text.minHeight() };
|
||||
}
|
||||
const auto height = text.countHeight(maxWidth);
|
||||
return { Ui::FindNiceTooltipWidth(minWidth, maxWidth, [&](int width) {
|
||||
return text.countHeight(width);
|
||||
}), height };
|
||||
}
|
||||
|
||||
bool TextRows::isEmpty() const {
|
||||
return _rows.empty()
|
||||
|| (_rows.size() == 1 && _rows.front().text.isEmpty());
|
||||
}
|
||||
|
||||
int TextRows::maxWidth() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TextRows::minHeight() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TextRows::countHeight(int newWidth) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TextRows::length() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TextSelection UnshiftItemSelection(
|
||||
TextSelection selection,
|
||||
const TextRows &byText) {
|
||||
return UnshiftItemSelection(selection, byText.length());
|
||||
}
|
||||
|
||||
TextSelection ShiftItemSelection(
|
||||
TextSelection selection,
|
||||
const TextRows &byText) {
|
||||
return ShiftItemSelection(selection, byText.length());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Giveaway::Giveaway(
|
||||
not_null<Element*> parent,
|
||||
not_null<Data::Giveaway*> giveaway)
|
||||
: Media(parent) {
|
||||
: Media(parent)
|
||||
, _prizesTitle(st::msgMinWidth)
|
||||
, _prizes(st::msgMinWidth)
|
||||
, _participantsTitle(st::msgMinWidth)
|
||||
, _participants(st::msgMinWidth)
|
||||
, _winnersTitle(st::msgMinWidth)
|
||||
, _winners(st::msgMinWidth) {
|
||||
fillFromData(giveaway);
|
||||
|
||||
if (!parent->data()->Has<HistoryMessageForwarded>()
|
||||
&& ranges::contains(
|
||||
giveaway->channels,
|
||||
parent->data()->history()->peer)) {
|
||||
parent->setServicePreMessage({
|
||||
tr::lng_action_giveaway_started(
|
||||
tr::now,
|
||||
lt_from,
|
||||
parent->data()->history()->peer->name()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Giveaway::~Giveaway() = default;
|
||||
Giveaway::~Giveaway() {
|
||||
if (hasHeavyPart()) {
|
||||
unloadHeavyPart();
|
||||
_parent->checkHeavyPart();
|
||||
}
|
||||
}
|
||||
|
||||
void Giveaway::fillFromData(not_null<Data::Giveaway*> giveaway) {
|
||||
_rows.add(Ui::Text::String(
|
||||
_months = giveaway->months;
|
||||
|
||||
_prizesTitle.setText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_prizes_title(tr::now, lt_count, giveaway->quantity),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth), st::chatGiveawayPrizesTop);
|
||||
kDefaultTextOptions);
|
||||
|
||||
const auto duration = (giveaway->months < 12)
|
||||
? tr::lng_months(tr::now, lt_count, giveaway->months)
|
||||
: tr::lng_years(tr::now, lt_count, giveaway->months / 12);
|
||||
_rows.add(Ui::Text::String(
|
||||
_prizes.setMarkedText(
|
||||
st::defaultTextStyle,
|
||||
tr::lng_prizes_about(
|
||||
tr::now,
|
||||
|
@ -84,94 +101,123 @@ void Giveaway::fillFromData(not_null<Data::Giveaway*> giveaway) {
|
|||
lt_duration,
|
||||
Ui::Text::Bold(duration),
|
||||
Ui::Text::RichLangValue),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth), st::chatGiveawayPrizesSkip);
|
||||
|
||||
_rows.add(Ui::Text::String(
|
||||
kDefaultTextOptions);
|
||||
_participantsTitle.setText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_prizes_participants(tr::now),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth), st::chatGiveawayParticipantsTop);
|
||||
kDefaultTextOptions);
|
||||
|
||||
for (const auto &channel : giveaway->channels) {
|
||||
_channels.push_back({ Ui::Text::String(
|
||||
st::semiboldTextStyle,
|
||||
channel->name(),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth)
|
||||
_channels.push_back({
|
||||
.name = Ui::Text::String(
|
||||
st::semiboldTextStyle,
|
||||
channel->name(),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth),
|
||||
.thumbnail = Dialogs::Stories::MakeUserpicThumbnail(channel),
|
||||
.link = channel->openLink(),
|
||||
});
|
||||
}
|
||||
|
||||
const auto channels = int(_channels.size());
|
||||
_rows.add(Ui::Text::String(
|
||||
|
||||
_participants.setText(
|
||||
st::defaultTextStyle,
|
||||
(giveaway->all
|
||||
? tr::lng_prizes_participants_all
|
||||
: tr::lng_prizes_participants_new)(tr::now, lt_count, channels),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth), st::chatGiveawayParticipantsSkip);
|
||||
|
||||
_date.add(Ui::Text::String(
|
||||
kDefaultTextOptions);
|
||||
_winnersTitle.setText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_prizes_date(tr::now),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth), st::chatGiveawayDateTop);
|
||||
|
||||
_rows.add(Ui::Text::String(
|
||||
kDefaultTextOptions);
|
||||
_winners.setText(
|
||||
st::defaultTextStyle,
|
||||
langDateTime(base::unixtime::parse(giveaway->untilDate)),
|
||||
kDefaultTextOptions,
|
||||
st::msgMinWidth), st::chatGiveawayDateSkip);
|
||||
kDefaultTextOptions);
|
||||
|
||||
ensureStickerCreated();
|
||||
}
|
||||
|
||||
QSize Giveaway::countOptimalSize() {
|
||||
// init dimensions
|
||||
auto skipBlockWidth = _parent->skipBlockWidth();
|
||||
auto maxWidth = skipBlockWidth;
|
||||
auto minHeight = 0;
|
||||
const auto maxWidth = st::chatGiveawayWidth;
|
||||
const auto padding = inBubblePadding();
|
||||
const auto available = maxWidth - padding.left() - padding.right();
|
||||
|
||||
accumulate_max(maxWidth, _rows.maxWidth());
|
||||
minHeight += _rows.minHeight();
|
||||
_stickerTop = st::chatGiveawayStickerTop;
|
||||
_prizesTitleTop = _stickerTop
|
||||
+ st::msgServiceGiftBoxStickerSize.height()
|
||||
+ st::chatGiveawayPrizesTop;
|
||||
_prizesTop = _prizesTitleTop
|
||||
+ _prizesTitle.countHeight(available)
|
||||
+ st::chatGiveawayPrizesSkip;
|
||||
const auto prizesSize = CountOptimalTextSize(
|
||||
_prizes,
|
||||
st::msgMinWidth,
|
||||
available);
|
||||
_prizesWidth = prizesSize.width();
|
||||
_participantsTitleTop = _prizesTop
|
||||
+ prizesSize.height()
|
||||
+ st::chatGiveawayParticipantsTop;
|
||||
_participantsTop = _participantsTitleTop
|
||||
+ _participantsTitle.countHeight(available)
|
||||
+ st::chatGiveawayParticipantsSkip;
|
||||
const auto participantsSize = CountOptimalTextSize(
|
||||
_participants,
|
||||
st::msgMinWidth,
|
||||
available);
|
||||
_participantsWidth = participantsSize.width();
|
||||
const auto channelsTop = _participantsTop
|
||||
+ participantsSize.height()
|
||||
+ st::chatGiveawayChannelTop;
|
||||
const auto channelsBottom = layoutChannels(
|
||||
padding.left(),
|
||||
channelsTop,
|
||||
available);
|
||||
_winnersTitleTop = channelsBottom + st::chatGiveawayDateTop;
|
||||
_winnersTop = _winnersTitleTop
|
||||
+ _winnersTitle.countHeight(available)
|
||||
+ st::chatGiveawayDateSkip;
|
||||
const auto height = _winnersTop
|
||||
+ _winners.countHeight(available)
|
||||
+ st::chatGiveawayBottomSkip;
|
||||
return { maxWidth, height };
|
||||
}
|
||||
|
||||
//minHeight +=
|
||||
|
||||
accumulate_max(maxWidth, _date.maxWidth());
|
||||
minHeight += _date.minHeight();
|
||||
|
||||
auto padding = inBubblePadding();
|
||||
maxWidth += padding.left() + padding.right();
|
||||
minHeight += padding.top() + padding.bottom();
|
||||
return { maxWidth, minHeight };
|
||||
int Giveaway::layoutChannels(int x, int y, int available) {
|
||||
const auto size = st::chatGiveawayChannelSize;
|
||||
const auto skip = st::chatGiveawayChannelSkip;
|
||||
const auto padding = st::chatGiveawayChannelPadding;
|
||||
auto left = available;
|
||||
const auto shiftRow = [&](int i, int top, int shift) {
|
||||
for (auto j = i; j != 0; --j) {
|
||||
auto &geometry = _channels[j - 1].geometry;
|
||||
if (geometry.top() != top) {
|
||||
break;
|
||||
}
|
||||
geometry.moveLeft(geometry.x() + shift);
|
||||
}
|
||||
};
|
||||
const auto count = int(_channels.size());
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
const auto desired = size
|
||||
+ padding.left()
|
||||
+ _channels[i].name.maxWidth()
|
||||
+ padding.right();
|
||||
const auto width = std::min(desired, available);
|
||||
if (left < width) {
|
||||
shiftRow(i, y, (left + skip) / 2);
|
||||
left = available;
|
||||
y += size + skip;
|
||||
}
|
||||
_channels[i].geometry = { x + available - left, y, width, size };
|
||||
left -= width + skip;
|
||||
}
|
||||
shiftRow(count, y, (left + skip) / 2);
|
||||
return y + size + skip;
|
||||
}
|
||||
|
||||
QSize Giveaway::countCurrentSize(int newWidth) {
|
||||
accumulate_min(newWidth, maxWidth());
|
||||
auto innerWidth = newWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
|
||||
auto newHeight = 0;
|
||||
_rowsHeight = _rows.countHeight(innerWidth);
|
||||
newHeight += _rowsHeight;
|
||||
|
||||
//newHeight +=
|
||||
|
||||
newHeight += _date.countHeight(innerWidth);
|
||||
_dateHeight = _date.minHeight();
|
||||
newHeight += _dateHeight;
|
||||
|
||||
auto padding = inBubblePadding();
|
||||
newHeight += padding.top() + padding.bottom();
|
||||
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
TextSelection Giveaway::toDateSelection(TextSelection selection) const {
|
||||
return UnshiftItemSelection(selection, _rows);
|
||||
}
|
||||
|
||||
TextSelection Giveaway::fromDateSelection(TextSelection selection) const {
|
||||
return ShiftItemSelection(selection, _rows);
|
||||
return { maxWidth(), minHeight()};
|
||||
}
|
||||
|
||||
void Giveaway::draw(Painter &p, const PaintContext &context) const {
|
||||
|
@ -184,24 +230,107 @@ void Giveaway::draw(Painter &p, const PaintContext &context) const {
|
|||
auto &semibold = stm->msgServiceFg;
|
||||
|
||||
auto padding = inBubblePadding();
|
||||
auto tshift = padding.top();
|
||||
|
||||
//_rows.draw(p, {
|
||||
// .position = { padding.left(), tshift },
|
||||
// .outerWidth = width(),
|
||||
// .availableWidth = paintw,
|
||||
// .now = context.now,
|
||||
// .selection = context.selection,
|
||||
//});
|
||||
//tshift += _rows.countHeight(paintw);
|
||||
const auto outer = width();
|
||||
const auto paintw = outer - padding.left() - padding.right();
|
||||
const auto stickerSize = st::msgServiceGiftBoxStickerSize;
|
||||
const auto sticker = QRect(
|
||||
(outer - stickerSize.width()) / 2,
|
||||
_stickerTop,
|
||||
stickerSize.width(),
|
||||
stickerSize.height());
|
||||
|
||||
//_date.draw(p, {
|
||||
// .position = { padding.left(), tshift },
|
||||
// .outerWidth = width(),
|
||||
// .availableWidth = paintw,
|
||||
// .now = context.now,
|
||||
// .selection = toDateSelection(context.selection),
|
||||
//});
|
||||
if (_sticker) {
|
||||
_sticker->draw(p, context, sticker);
|
||||
} else {
|
||||
ensureStickerCreated();
|
||||
}
|
||||
const auto paintText = [&](
|
||||
const Ui::Text::String &text,
|
||||
int top,
|
||||
int width) {
|
||||
p.setPen(stm->historyTextFg);
|
||||
text.draw(p, {
|
||||
.position = { padding.left() + (paintw - width) / 2, top},
|
||||
.outerWidth = outer,
|
||||
.availableWidth = width,
|
||||
.align = style::al_top,
|
||||
.palette = &stm->textPalette,
|
||||
.now = context.now,
|
||||
});
|
||||
};
|
||||
paintText(_prizesTitle, _prizesTitleTop, paintw);
|
||||
paintText(_prizes, _prizesTop, _prizesWidth);
|
||||
paintText(_participantsTitle, _participantsTitleTop, paintw);
|
||||
paintText(_participants, _participantsTop, _participantsWidth);
|
||||
paintText(_winnersTitle, _winnersTitleTop, paintw);
|
||||
paintText(_winners, _winnersTop, paintw);
|
||||
paintChannels(p, context);
|
||||
}
|
||||
|
||||
void Giveaway::paintChannels(
|
||||
Painter &p,
|
||||
const PaintContext &context) const {
|
||||
if (_channels.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size = _channels[0].geometry.height();
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto stm = context.messageStyle();
|
||||
auto bg = stm->msgReplyBarColor->c;
|
||||
bg.setAlpha(kChannelBgAlpha);
|
||||
if (_channelCorners[0].isNull() || _channelBg != bg) {
|
||||
_channelBg = bg;
|
||||
_channelCorners = Images::CornersMask(size / 2);
|
||||
for (auto &image : _channelCorners) {
|
||||
style::colorizeImage(image, bg, &image);
|
||||
}
|
||||
}
|
||||
p.setPen(stm->msgReplyBarColor);
|
||||
const auto padding = st::chatGiveawayChannelPadding;
|
||||
for (const auto &channel : _channels) {
|
||||
const auto &thumbnail = channel.thumbnail;
|
||||
const auto &geometry = channel.geometry;
|
||||
if (!_subscribedToThumbnails) {
|
||||
thumbnail->subscribeToUpdates([view = parent()] {
|
||||
view->history()->owner().requestViewRepaint(view);
|
||||
});
|
||||
}
|
||||
Ui::DrawRoundedRect(p, geometry, _channelBg, _channelCorners);
|
||||
p.drawImage(geometry.topLeft(), thumbnail->image(size));
|
||||
const auto left = size + padding.left();
|
||||
const auto top = padding.top();
|
||||
const auto available = geometry.width() - left - padding.right();
|
||||
channel.name.draw(p, {
|
||||
.position = { geometry.left() + left, geometry.top() + top },
|
||||
.outerWidth = width(),
|
||||
.availableWidth = available,
|
||||
.align = style::al_left,
|
||||
.palette = &stm->textPalette,
|
||||
.now = context.now,
|
||||
.elisionOneLine = true,
|
||||
.elisionBreakEverywhere = true,
|
||||
});
|
||||
}
|
||||
_subscribedToThumbnails = true;
|
||||
}
|
||||
|
||||
void Giveaway::ensureStickerCreated() const {
|
||||
if (_sticker) {
|
||||
return;
|
||||
}
|
||||
const auto &session = _parent->history()->session();
|
||||
auto &packs = session.giftBoxStickersPacks();
|
||||
if (const auto document = packs.lookup(_months)) {
|
||||
if (const auto sticker = document->sticker()) {
|
||||
const auto skipPremiumEffect = false;
|
||||
_sticker.emplace(_parent, document, skipPremiumEffect, _parent);
|
||||
_sticker->setDiceIndex(sticker->alt, 1);
|
||||
_sticker->setGiftBoxSticker(true);
|
||||
_sticker->initSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextState Giveaway::textState(QPoint point, StateRequest request) const {
|
||||
|
@ -211,80 +340,29 @@ TextState Giveaway::textState(QPoint point, StateRequest request) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
auto padding = inBubblePadding();
|
||||
auto tshift = padding.top();
|
||||
auto bshift = padding.bottom();
|
||||
|
||||
auto symbolAdd = 0;
|
||||
if (_rowsHeight > 0) {
|
||||
if (point.y() >= tshift && point.y() < tshift + _rowsHeight) {
|
||||
//result = TextState(_parent, _rows.getState(
|
||||
// point - QPoint(padding.left(), tshift),
|
||||
// paintw,
|
||||
// width(),
|
||||
// request.forText()));
|
||||
} else if (point.y() >= tshift + _rowsHeight) {
|
||||
symbolAdd += _rows.length();
|
||||
for (const auto &channel : _channels) {
|
||||
if (channel.geometry.contains(point)) {
|
||||
result.link = channel.link;
|
||||
return result;
|
||||
}
|
||||
tshift += _rowsHeight;
|
||||
}
|
||||
if (_channelsHeight > 0) {
|
||||
tshift += _channelsHeight;
|
||||
}
|
||||
if (_dateHeight > 0) {
|
||||
if (point.y() >= tshift && point.y() < tshift + _dateHeight) {
|
||||
//result = TextState(_parent, _date.getState(
|
||||
// point - QPoint(padding.left(), tshift),
|
||||
// paintw,
|
||||
// width(),
|
||||
// request.forText()));
|
||||
} else if (point.y() >= tshift + _dateHeight) {
|
||||
symbolAdd += _date.length();
|
||||
}
|
||||
tshift += _dateHeight;
|
||||
}
|
||||
result.symbol += symbolAdd;
|
||||
return result;
|
||||
}
|
||||
|
||||
TextSelection Giveaway::adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const {
|
||||
//if (_date.isEmpty() || selection.to <= _rows.length()) {
|
||||
// return _rows.adjustSelection(selection, type);
|
||||
//}
|
||||
//const auto dateSelection = _date.adjustSelection(
|
||||
// toDateSelection(selection),
|
||||
// type);
|
||||
//if (selection.from >= _rows.length()) {
|
||||
// return fromDateSelection(dateSelection);
|
||||
//}
|
||||
//const auto rowsSelection = _rows.adjustSelection(selection, type);
|
||||
//return { rowsSelection.from, fromDateSelection(dateSelection).to };
|
||||
return selection;
|
||||
bool Giveaway::hideFromName() const {
|
||||
return !parent()->data()->Has<HistoryMessageForwarded>();
|
||||
}
|
||||
|
||||
bool Giveaway::hasHeavyPart() const {
|
||||
return false;
|
||||
return _subscribedToThumbnails;
|
||||
}
|
||||
|
||||
void Giveaway::unloadHeavyPart() {
|
||||
}
|
||||
|
||||
uint16 Giveaway::fullSelectionLength() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TextForMimeData Giveaway::selectedText(TextSelection selection) const {
|
||||
//auto rowsResult = _rows.toTextForMimeData(selection);
|
||||
//auto dateResult = _date.toTextForMimeData(toDateSelection(selection));
|
||||
//if (rowsResult.empty()) {
|
||||
// return dateResult;
|
||||
//} else if (dateResult.empty()) {
|
||||
// return rowsResult;
|
||||
//}
|
||||
//return rowsResult.append('\n').append(std::move(dateResult));
|
||||
return {};
|
||||
if (base::take(_subscribedToThumbnails)) {
|
||||
for (const auto &channel : _channels) {
|
||||
channel.thumbnail->subscribeToUpdates(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMargins Giveaway::inBubblePadding() const {
|
||||
|
|
|
@ -8,40 +8,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
|
||||
namespace Data {
|
||||
struct Giveaway;
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs::Stories {
|
||||
class Thumbnail;
|
||||
} // namespace Dialogs::Stories
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class TextRows final {
|
||||
public:
|
||||
void add(Ui::Text::String text, int skipTop);
|
||||
|
||||
[[nodiscard]] bool isEmpty() const;
|
||||
[[nodiscard]] int maxWidth() const;
|
||||
[[nodiscard]] int minHeight() const;
|
||||
|
||||
[[nodiscard]] int countHeight(int newWidth) const;
|
||||
|
||||
[[nodiscard]] int length() const;
|
||||
|
||||
private:
|
||||
struct Row {
|
||||
Ui::Text::String text;
|
||||
int skipTop = 0;
|
||||
};
|
||||
std::vector<Row> _rows;
|
||||
};
|
||||
|
||||
[[nodiscard]] TextSelection UnshiftItemSelection(
|
||||
TextSelection selection,
|
||||
const TextRows &byText);
|
||||
[[nodiscard]] TextSelection ShiftItemSelection(
|
||||
TextSelection selection,
|
||||
const TextRows &byText);
|
||||
|
||||
class Giveaway final : public Media {
|
||||
public:
|
||||
Giveaway(
|
||||
|
@ -67,35 +45,54 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] TextSelection adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const override;
|
||||
uint16 fullSelectionLength() const override;
|
||||
TextForMimeData selectedText(TextSelection selection) const override;
|
||||
bool hideFromName() const override;
|
||||
|
||||
void unloadHeavyPart() override;
|
||||
bool hasHeavyPart() const override;
|
||||
|
||||
private:
|
||||
using Thumbnail = Dialogs::Stories::Thumbnail;
|
||||
struct Channel {
|
||||
Ui::Text::String name;
|
||||
std::shared_ptr<Thumbnail> thumbnail;
|
||||
QRect geometry;
|
||||
ClickHandlerPtr link;
|
||||
};
|
||||
|
||||
void paintChannels(Painter &p, const PaintContext &context) const;
|
||||
int layoutChannels(int x, int y, int available);
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
||||
void fillFromData(not_null<Data::Giveaway*> giveaway);
|
||||
void ensureStickerCreated() const;
|
||||
|
||||
TextSelection toDateSelection(TextSelection selection) const;
|
||||
TextSelection fromDateSelection(TextSelection selection) const;
|
||||
QMargins inBubblePadding() const;
|
||||
[[nodiscard]] QMargins inBubblePadding() const;
|
||||
|
||||
TextRows _rows;
|
||||
mutable std::optional<Sticker> _sticker;
|
||||
|
||||
Ui::Text::String _prizesTitle;
|
||||
Ui::Text::String _prizes;
|
||||
Ui::Text::String _participantsTitle;
|
||||
Ui::Text::String _participants;
|
||||
std::vector<Channel> _channels;
|
||||
TextRows _date;
|
||||
int _rowsHeight = 0;
|
||||
int _channelsHeight = 0;
|
||||
int _dateHeight = 0;
|
||||
Ui::Text::String _winnersTitle;
|
||||
Ui::Text::String _winners;
|
||||
|
||||
mutable QColor _channelBg;
|
||||
mutable std::array<QImage, 4> _channelCorners;
|
||||
|
||||
int _months = 0;
|
||||
int _stickerTop = 0;
|
||||
int _prizesTitleTop = 0;
|
||||
int _prizesTop = 0;
|
||||
int _prizesWidth = 0;
|
||||
int _participantsTitleTop = 0;
|
||||
int _participantsTop = 0;
|
||||
int _participantsWidth = 0;
|
||||
int _winnersTitleTop = 0;
|
||||
int _winnersTop = 0;
|
||||
mutable bool _subscribedToThumbnails = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -101,6 +101,9 @@ public:
|
|||
[[nodiscard]] virtual bool hideServiceText() const {
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] virtual bool hideFromName() const {
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] virtual bool allowsFastShare() const {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace {
|
|||
struct SessionProcesses {
|
||||
base::flat_map<FullMsgId, std::unique_ptr<CheckoutProcess>> byItem;
|
||||
base::flat_map<QString, std::unique_ptr<CheckoutProcess>> bySlug;
|
||||
base::flat_map<uint64, std::unique_ptr<CheckoutProcess>> byRandomId;
|
||||
base::flat_map<FullMsgId, PaidInvoice> paymentStartedByItem;
|
||||
base::flat_map<QString, PaidInvoice> paymentStartedBySlug;
|
||||
rpl::lifetime lifetime;
|
||||
|
@ -118,6 +119,28 @@ void CheckoutProcess::Start(
|
|||
j->second->requestActivate();
|
||||
}
|
||||
|
||||
void CheckoutProcess::Start(
|
||||
InvoicePremiumGiftCode giftCodeInvoice,
|
||||
Fn<void(CheckoutResult)> reactivate) {
|
||||
const auto randomId = giftCodeInvoice.randomId;
|
||||
auto id = InvoiceId{ std::move(giftCodeInvoice) };
|
||||
auto &processes = LookupSessionProcesses(SessionFromId(id));
|
||||
const auto i = processes.byRandomId.find(randomId);
|
||||
if (i != end(processes.byRandomId)) {
|
||||
i->second->setReactivateCallback(std::move(reactivate));
|
||||
i->second->requestActivate();
|
||||
return;
|
||||
}
|
||||
const auto j = processes.byRandomId.emplace(
|
||||
randomId,
|
||||
std::make_unique<CheckoutProcess>(
|
||||
std::move(id),
|
||||
Mode::Payment,
|
||||
std::move(reactivate),
|
||||
PrivateTag{})).first;
|
||||
j->second->requestActivate();
|
||||
}
|
||||
|
||||
std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
|
||||
not_null<const HistoryItem*> item) {
|
||||
const auto session = &item->history()->session();
|
||||
|
@ -139,7 +162,8 @@ std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
|
|||
} else if (i->second.paymentStartedByItem.empty()
|
||||
&& i->second.byItem.empty()
|
||||
&& i->second.paymentStartedBySlug.empty()
|
||||
&& i->second.bySlug.empty()) {
|
||||
&& i->second.bySlug.empty()
|
||||
&& i->second.byRandomId.empty()) {
|
||||
Processes.erase(i);
|
||||
}
|
||||
return result;
|
||||
|
@ -165,7 +189,8 @@ std::optional<PaidInvoice> CheckoutProcess::InvoicePaid(
|
|||
} else if (i->second.paymentStartedByItem.empty()
|
||||
&& i->second.byItem.empty()
|
||||
&& i->second.paymentStartedBySlug.empty()
|
||||
&& i->second.bySlug.empty()) {
|
||||
&& i->second.bySlug.empty()
|
||||
&& i->second.byRandomId.empty()) {
|
||||
Processes.erase(i);
|
||||
}
|
||||
return result;
|
||||
|
@ -192,6 +217,11 @@ void CheckoutProcess::RegisterPaymentStart(
|
|||
return;
|
||||
}
|
||||
}
|
||||
for (const auto &[randomId, itemProcess] : i->second.byRandomId) {
|
||||
if (itemProcess.get() == process) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckoutProcess::UnregisterPaymentStart(
|
||||
|
@ -212,10 +242,16 @@ void CheckoutProcess::UnregisterPaymentStart(
|
|||
break;
|
||||
}
|
||||
}
|
||||
for (const auto &[randomId, itemProcess] : i->second.byRandomId) {
|
||||
if (itemProcess.get() == process) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i->second.paymentStartedByItem.empty()
|
||||
&& i->second.byItem.empty()
|
||||
&& i->second.paymentStartedBySlug.empty()
|
||||
&& i->second.bySlug.empty()) {
|
||||
&& i->second.bySlug.empty()
|
||||
&& i->second.byRandomId.empty()) {
|
||||
Processes.erase(i);
|
||||
}
|
||||
}
|
||||
|
@ -497,8 +533,16 @@ void CheckoutProcess::close() {
|
|||
if (k != end(entry.bySlug)) {
|
||||
entry.bySlug.erase(k);
|
||||
}
|
||||
const auto l = ranges::find(
|
||||
entry.byRandomId,
|
||||
this,
|
||||
[](const auto &pair) { return pair.second.get(); });
|
||||
if (l != end(entry.byRandomId)) {
|
||||
entry.byRandomId.erase(l);
|
||||
}
|
||||
if (entry.byItem.empty()
|
||||
&& entry.bySlug.empty()
|
||||
&& i->second.byRandomId.empty()
|
||||
&& entry.paymentStartedByItem.empty()
|
||||
&& entry.paymentStartedBySlug.empty()) {
|
||||
Processes.erase(i);
|
||||
|
|
|
@ -37,6 +37,7 @@ class Form;
|
|||
struct FormUpdate;
|
||||
struct Error;
|
||||
struct InvoiceId;
|
||||
struct InvoicePremiumGiftCode;
|
||||
|
||||
enum class Mode {
|
||||
Payment,
|
||||
|
@ -68,6 +69,9 @@ public:
|
|||
not_null<Main::Session*> session,
|
||||
const QString &slug,
|
||||
Fn<void(CheckoutResult)> reactivate);
|
||||
static void Start(
|
||||
InvoicePremiumGiftCode giftCodeInvoice,
|
||||
Fn<void(CheckoutResult)> reactivate);
|
||||
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
||||
not_null<const HistoryItem*> item);
|
||||
[[nodiscard]] static std::optional<PaidInvoice> InvoicePaid(
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "payments/payments_form.h"
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -112,10 +113,21 @@ constexpr auto kPasswordPeriod = 15 * TimeId(60);
|
|||
} // namespace
|
||||
|
||||
not_null<Main::Session*> SessionFromId(const InvoiceId &id) {
|
||||
if (const auto slug = std::get_if<InvoiceSlug>(&id.value)) {
|
||||
if (const auto message = std::get_if<InvoiceMessage>(&id.value)) {
|
||||
return &message->peer->session();
|
||||
} else if (const auto slug = std::get_if<InvoiceSlug>(&id.value)) {
|
||||
return slug->session;
|
||||
}
|
||||
return &v::get<InvoiceMessage>(id.value).peer->session();
|
||||
const auto &giftCode = v::get<InvoicePremiumGiftCode>(id.value);
|
||||
const auto users = std::get_if<InvoicePremiumGiftCodeUsers>(
|
||||
&giftCode.purpose);
|
||||
if (users) {
|
||||
Assert(!users->users.empty());
|
||||
return &users->users.front()->session();
|
||||
}
|
||||
const auto &giveaway = v::get<InvoicePremiumGiftCodeGiveaway>(
|
||||
giftCode.purpose);
|
||||
return &giveaway.boostPeer->session();
|
||||
}
|
||||
|
||||
Form::Form(InvoiceId id, bool receipt)
|
||||
|
@ -207,11 +219,10 @@ void Form::loadThumbnail(not_null<PhotoData*> photo) {
|
|||
}
|
||||
|
||||
Data::FileOrigin Form::thumbnailFileOrigin() const {
|
||||
if (const auto slug = std::get_if<InvoiceSlug>(&_id.value)) {
|
||||
return Data::FileOrigin();
|
||||
if (const auto message = std::get_if<InvoiceMessage>(&_id.value)) {
|
||||
return FullMsgId(message->peer->id, message->itemId);
|
||||
}
|
||||
const auto message = v::get<InvoiceMessage>(_id.value);
|
||||
return FullMsgId(message.peer->id, message.itemId);
|
||||
return Data::FileOrigin();
|
||||
}
|
||||
|
||||
QImage Form::prepareGoodThumbnail(
|
||||
|
@ -257,13 +268,67 @@ QImage Form::prepareEmptyThumbnail() const {
|
|||
}
|
||||
|
||||
MTPInputInvoice Form::inputInvoice() const {
|
||||
if (const auto slug = std::get_if<InvoiceSlug>(&_id.value)) {
|
||||
if (const auto message = std::get_if<InvoiceMessage>(&_id.value)) {
|
||||
return MTP_inputInvoiceMessage(
|
||||
message->peer->input,
|
||||
MTP_int(message->itemId.bare));
|
||||
} else if (const auto slug = std::get_if<InvoiceSlug>(&_id.value)) {
|
||||
return MTP_inputInvoiceSlug(MTP_string(slug->slug));
|
||||
}
|
||||
const auto message = v::get<InvoiceMessage>(_id.value);
|
||||
return MTP_inputInvoiceMessage(
|
||||
message.peer->input,
|
||||
MTP_int(message.itemId.bare));
|
||||
const auto &giftCode = v::get<InvoicePremiumGiftCode>(_id.value);
|
||||
using Flag = MTPDpremiumGiftCodeOption::Flag;
|
||||
const auto option = MTP_premiumGiftCodeOption(
|
||||
MTP_flags((giftCode.storeQuantity ? Flag::f_store_quantity : Flag())
|
||||
| (giftCode.storeProduct.isEmpty()
|
||||
? Flag()
|
||||
: Flag::f_store_product)),
|
||||
MTP_int(giftCode.users),
|
||||
MTP_int(giftCode.months),
|
||||
MTP_string(giftCode.storeProduct),
|
||||
MTP_int(giftCode.storeQuantity),
|
||||
MTP_string(giftCode.currency),
|
||||
MTP_long(giftCode.amount));
|
||||
const auto users = std::get_if<InvoicePremiumGiftCodeUsers>(
|
||||
&giftCode.purpose);
|
||||
if (users) {
|
||||
using Flag = MTPDinputStorePaymentPremiumGiftCode::Flag;
|
||||
return MTP_inputInvoicePremiumGiftCode(
|
||||
MTP_inputStorePaymentPremiumGiftCode(
|
||||
MTP_flags(users->boostPeer ? Flag::f_boost_peer : Flag()),
|
||||
MTP_vector<MTPInputUser>(ranges::views::all(
|
||||
users->users
|
||||
) | ranges::views::transform([](not_null<UserData*> user) {
|
||||
return MTPInputUser(user->inputUser);
|
||||
}) | ranges::to<QVector>),
|
||||
users->boostPeer ? users->boostPeer->input : MTPInputPeer(),
|
||||
MTP_string(giftCode.currency),
|
||||
MTP_long(giftCode.amount)),
|
||||
option);
|
||||
} else {
|
||||
const auto &giveaway = v::get<InvoicePremiumGiftCodeGiveaway>(
|
||||
giftCode.purpose);
|
||||
using Flag = MTPDinputStorePaymentPremiumGiveaway::Flag;
|
||||
return MTP_inputInvoicePremiumGiftCode(
|
||||
MTP_inputStorePaymentPremiumGiveaway(
|
||||
MTP_flags(Flag()
|
||||
| (giveaway.onlyNewSubscribers
|
||||
? Flag::f_only_new_subscribers
|
||||
: Flag())
|
||||
| (giveaway.additionalChannels.empty()
|
||||
? Flag()
|
||||
: Flag::f_additional_peers)),
|
||||
giveaway.boostPeer->input,
|
||||
MTP_vector<MTPInputPeer>(ranges::views::all(
|
||||
giveaway.additionalChannels
|
||||
) | ranges::views::transform([](not_null<ChannelData*> c) {
|
||||
return MTPInputPeer(c->input);
|
||||
}) | ranges::to<QVector>()),
|
||||
MTP_long(giftCode.randomId),
|
||||
MTP_int(giveaway.untilDate),
|
||||
MTP_string(giftCode.currency),
|
||||
MTP_long(giftCode.amount)),
|
||||
option);
|
||||
}
|
||||
}
|
||||
|
||||
void Form::requestForm() {
|
||||
|
|
|
@ -186,8 +186,34 @@ struct InvoiceSlug {
|
|||
QString slug;
|
||||
};
|
||||
|
||||
struct InvoicePremiumGiftCodeGiveaway {
|
||||
not_null<ChannelData*> boostPeer;
|
||||
std::vector<not_null<ChannelData*>> additionalChannels;
|
||||
TimeId untilDate = 0;
|
||||
bool onlyNewSubscribers = false;
|
||||
};
|
||||
|
||||
struct InvoicePremiumGiftCodeUsers {
|
||||
std::vector<not_null<UserData*>> users;
|
||||
ChannelData *boostPeer = nullptr;
|
||||
};
|
||||
|
||||
struct InvoicePremiumGiftCode {
|
||||
std::variant<
|
||||
InvoicePremiumGiftCodeUsers,
|
||||
InvoicePremiumGiftCodeGiveaway> purpose;
|
||||
|
||||
uint64 randomId = 0;
|
||||
QString currency;
|
||||
uint64 amount = 0;
|
||||
QString storeProduct;
|
||||
int storeQuantity = 0;
|
||||
int users = 0;
|
||||
int months = 0;
|
||||
};
|
||||
|
||||
struct InvoiceId {
|
||||
std::variant<InvoiceMessage, InvoiceSlug> value;
|
||||
std::variant<InvoiceMessage, InvoiceSlug, InvoicePremiumGiftCode> value;
|
||||
};
|
||||
|
||||
[[nodiscard]] not_null<Main::Session*> SessionFromId(const InvoiceId &id);
|
||||
|
|
|
@ -932,9 +932,16 @@ storyMentionReadSkipTwice: 7px;
|
|||
storyMentionReadStrokeTwice: 3px;
|
||||
storyMentionButtonSkip: 5px;
|
||||
|
||||
chatGiveawayPrizesTop: 4px;
|
||||
chatGiveawayWidth: 292px;
|
||||
chatGiveawayStickerTop: -16px;
|
||||
chatGiveawayPrizesTop: 16px;
|
||||
chatGiveawayPrizesSkip: 4px;
|
||||
chatGiveawayParticipantsTop: 16px;
|
||||
chatGiveawayParticipantsSkip: 4px;
|
||||
chatGiveawayDateTop: 16px;
|
||||
chatGiveawayChannelTop: 6px;
|
||||
chatGiveawayChannelSize: 32px;
|
||||
chatGiveawayChannelPadding: margins(5px, 7px, 12px, 0px);
|
||||
chatGiveawayChannelSkip: 8px;
|
||||
chatGiveawayDateTop: 6px;
|
||||
chatGiveawayDateSkip: 4px;
|
||||
chatGiveawayBottomSkip: 16px;
|
||||
|
|
Loading…
Add table
Reference in a new issue