mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 05:07:10 +02:00
Support paid sending in ShareBox.
This commit is contained in:
parent
928be4151b
commit
7b7e18e752
20 changed files with 468 additions and 193 deletions
|
@ -2795,6 +2795,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_credits_small_balance_subscribe" = "Buy **Stars** and subscribe to **{channel}** and other channels.";
|
||||
"lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts.";
|
||||
"lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}.";
|
||||
"lng_credits_small_balance_for_messages" = "Buy **Stars** to send messages.";
|
||||
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
|
||||
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
|
||||
"lng_credits_enough" = "You have enough stars at the moment. {link}";
|
||||
|
@ -4834,6 +4835,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_payment_confirm_text#other" = "{name} charges **{count}** Stars per message.";
|
||||
"lng_payment_confirm_amount#one" = "**{count}** Star";
|
||||
"lng_payment_confirm_amount#other" = "**{count}** Stars";
|
||||
"lng_payment_confirm_users#one" = "You selected **{count}** user who charge Stars for messages.";
|
||||
"lng_payment_confirm_users#other" = "You selected **{count}** users who charge Stars for messages.";
|
||||
"lng_payment_confirm_chats#one" = "You selected **{count}** chat where you pay Stars for messages.";
|
||||
"lng_payment_confirm_chats#other" = "You selected **{count}** chats where you pay Stars for messages.";
|
||||
"lng_payment_confirm_sure#one" = "Would you like to pay {amount} to send **{count}** message?";
|
||||
"lng_payment_confirm_sure#other" = "Would you like to pay {amount} to send **{count}** messages?";
|
||||
"lng_payment_confirm_dont_ask" = "Don't ask me again";
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_text_entities.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/random.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_values.h"
|
||||
|
@ -714,12 +715,18 @@ rpl::producer<rpl::no_value, QString> SponsoredToggle::setToggled(bool v) {
|
|||
MessageMoneyRestriction ResolveMessageMoneyRestrictions(
|
||||
not_null<PeerData*> peer,
|
||||
History *maybeHistory) {
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
return {
|
||||
.starsPerMessage = channel->starsPerMessageChecked(),
|
||||
.known = true,
|
||||
};
|
||||
}
|
||||
const auto user = peer->asUser();
|
||||
if (!user) {
|
||||
return { .known = true };
|
||||
} else if (user->messageMoneyRestrictionsKnown()) {
|
||||
return {
|
||||
.starsPerMessage = user->starsPerMessage(),
|
||||
.starsPerMessage = user->starsPerMessageChecked(),
|
||||
.premiumRequired = (user->requiresPremiumToWrite()
|
||||
&& !user->session().premium()),
|
||||
.known = true,
|
||||
|
|
|
@ -250,6 +250,14 @@ struct MessageMoneyRestriction {
|
|||
int starsPerMessage = 0;
|
||||
bool premiumRequired = false;
|
||||
bool known = false;
|
||||
|
||||
explicit operator bool() const {
|
||||
return starsPerMessage != 0 || premiumRequired;
|
||||
}
|
||||
|
||||
friend inline bool operator==(
|
||||
const MessageMoneyRestriction &,
|
||||
const MessageMoneyRestriction &) = default;
|
||||
};
|
||||
[[nodiscard]] MessageMoneyRestriction ResolveMessageMoneyRestrictions(
|
||||
not_null<PeerData*> peer,
|
||||
|
|
|
@ -46,7 +46,7 @@ constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value;
|
|||
constexpr auto kMiniAppsRowId = PeerId(FakeChatId(BareId(2))).value;
|
||||
constexpr auto kGetPercent = 85;
|
||||
constexpr auto kStarsMin = 1;
|
||||
constexpr auto kStarsMax = 9000;
|
||||
constexpr auto kStarsMax = 10000;
|
||||
constexpr auto kDefaultChargeStars = 10;
|
||||
|
||||
using Exceptions = Api::UserPrivacy::Exceptions;
|
||||
|
|
|
@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/peer_list_controllers.h"
|
||||
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "api/api_premium.h" // MessageMoneyRestriction.
|
||||
#include "base/random.h"
|
||||
#include "boxes/filters/edit_filter_chats_list.h"
|
||||
#include "settings/settings_premium.h"
|
||||
|
@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "payments/ui/payments_reaction_box.h"
|
||||
#include "ui/effects/outline_segments.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "window/window_separate_id.h"
|
||||
|
@ -275,28 +276,56 @@ bool PeerListGlobalSearchController::isLoading() {
|
|||
return _timer.isActive() || _requestId;
|
||||
}
|
||||
|
||||
struct RecipientRow::Restriction {
|
||||
Api::MessageMoneyRestriction value;
|
||||
RestrictionBadgeCache cache;
|
||||
};
|
||||
|
||||
RecipientRow::RecipientRow(
|
||||
not_null<PeerData*> peer,
|
||||
const style::PeerListItem *maybeLockedSt,
|
||||
History *maybeHistory)
|
||||
: PeerListRow(peer)
|
||||
, _maybeHistory(maybeHistory)
|
||||
, _resolvePremiumRequired(maybeLockedSt != nullptr) {
|
||||
if (maybeLockedSt
|
||||
&& Api::ResolveMessageMoneyRestrictions(
|
||||
, _maybeLockedSt(maybeLockedSt) {
|
||||
if (_maybeLockedSt) {
|
||||
setRestriction(Api::ResolveMessageMoneyRestrictions(
|
||||
peer,
|
||||
maybeHistory).premiumRequired) {
|
||||
_lockedSt = maybeLockedSt;
|
||||
maybeHistory));
|
||||
}
|
||||
}
|
||||
|
||||
Api::MessageMoneyRestriction RecipientRow::restriction() const {
|
||||
return _restriction
|
||||
? _restriction->value
|
||||
: Api::MessageMoneyRestriction();
|
||||
}
|
||||
|
||||
void RecipientRow::setRestriction(Api::MessageMoneyRestriction restriction) {
|
||||
if (!restriction) {
|
||||
_restriction = nullptr;
|
||||
return;
|
||||
} else if (!_restriction) {
|
||||
_restriction = std::make_unique<Restriction>();
|
||||
}
|
||||
_restriction->value = restriction;
|
||||
}
|
||||
|
||||
PaintRoundImageCallback RecipientRow::generatePaintUserpicCallback(
|
||||
bool forceRound) {
|
||||
auto result = PeerListRow::generatePaintUserpicCallback(forceRound);
|
||||
if (const auto st = _lockedSt) {
|
||||
if (const auto &r = _restriction) {
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
result(p, x, y, outerWidth, size);
|
||||
PaintPremiumRequiredLock(p, st, x, y, outerWidth, size);
|
||||
PaintRestrictionBadge(
|
||||
p,
|
||||
_maybeLockedSt,
|
||||
r->value.starsPerMessage,
|
||||
r->cache,
|
||||
x,
|
||||
y,
|
||||
outerWidth,
|
||||
size);
|
||||
};
|
||||
}
|
||||
return result;
|
||||
|
@ -305,12 +334,14 @@ PaintRoundImageCallback RecipientRow::generatePaintUserpicCallback(
|
|||
bool RecipientRow::refreshLock(
|
||||
not_null<const style::PeerListItem*> maybeLockedSt) {
|
||||
if (const auto user = peer()->asUser()) {
|
||||
const auto locked = _resolvePremiumRequired
|
||||
&& Api::ResolveMessageMoneyRestrictions(
|
||||
using Restriction = Api::MessageMoneyRestriction;
|
||||
const auto r = _maybeLockedSt
|
||||
? Api::ResolveMessageMoneyRestrictions(
|
||||
user,
|
||||
_maybeHistory).premiumRequired;
|
||||
if (this->locked() != locked) {
|
||||
setLocked(locked ? maybeLockedSt.get() : nullptr);
|
||||
_maybeHistory)
|
||||
: Restriction();
|
||||
if ((_restriction ? _restriction->value : Restriction()) != r) {
|
||||
setRestriction(r);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -320,14 +351,20 @@ bool RecipientRow::refreshLock(
|
|||
void RecipientRow::preloadUserpic() {
|
||||
PeerListRow::preloadUserpic();
|
||||
|
||||
if (!_resolvePremiumRequired) {
|
||||
if (!_maybeLockedSt) {
|
||||
return;
|
||||
} else if (!Api::ResolveMessageMoneyRestrictions(
|
||||
peer(),
|
||||
_maybeHistory).known) {
|
||||
const auto user = peer()->asUser();
|
||||
user->session().api().premium().resolveMessageMoneyRestrictions(
|
||||
user);
|
||||
}
|
||||
const auto peer = this->peer();
|
||||
const auto known = Api::ResolveMessageMoneyRestrictions(
|
||||
peer,
|
||||
_maybeHistory).known;
|
||||
if (known) {
|
||||
return;
|
||||
} else if (const auto user = peer->asUser()) {
|
||||
const auto api = &user->session().api();
|
||||
api->premium().resolveMessageMoneyRestrictions(user);
|
||||
} else if (const auto group = peer->asChannel()) {
|
||||
group->updateFull();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -844,7 +881,8 @@ bool RecipientRow::ShowLockedError(
|
|||
not_null<PeerListController*> controller,
|
||||
not_null<PeerListRow*> row,
|
||||
Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> error) {
|
||||
if (!static_cast<RecipientRow*>(row.get())->locked()) {
|
||||
const auto recipient = static_cast<RecipientRow*>(row.get());
|
||||
if (!recipient->restriction().premiumRequired) {
|
||||
return false;
|
||||
}
|
||||
::Settings::ShowPremiumPromoToast(
|
||||
|
@ -1100,25 +1138,61 @@ auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
|
|||
return skip ? nullptr : std::make_unique<Row>(topic);
|
||||
};
|
||||
|
||||
void PaintPremiumRequiredLock(
|
||||
void PaintRestrictionBadge(
|
||||
Painter &p,
|
||||
not_null<const style::PeerListItem*> st,
|
||||
int stars,
|
||||
RestrictionBadgeCache &cache,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto paletteVersion = style::PaletteVersion();
|
||||
const auto good = !cache.badge.isNull()
|
||||
&& (cache.stars == stars)
|
||||
&& (cache.paletteVersion == paletteVersion);
|
||||
const auto &check = st->checkbox.check;
|
||||
auto pen = check.border->p;
|
||||
pen.setWidthF(check.width);
|
||||
p.setPen(pen);
|
||||
p.setBrush(st::premiumButtonBg2);
|
||||
const auto &icon = st::stickersPremiumLock;
|
||||
const auto width = icon.width();
|
||||
const auto height = icon.height();
|
||||
const auto rect = QRect(
|
||||
QPoint(x + size - width, y + size - height),
|
||||
icon.size());
|
||||
p.drawEllipse(rect);
|
||||
icon.paintInCenter(p, rect);
|
||||
const auto add = check.width;
|
||||
if (!good) {
|
||||
cache.stars = stars;
|
||||
cache.paletteVersion = paletteVersion;
|
||||
if (stars) {
|
||||
const auto text = (stars >= 1000)
|
||||
? (QString::number(stars / 1000) + 'K')
|
||||
: QString::number(stars);
|
||||
cache.badge = Ui::GenerateSmallBadgeImage(
|
||||
text,
|
||||
st::paidReactTopStarIcon,
|
||||
check.bgActive->c,
|
||||
st::premiumButtonFg->c,
|
||||
&check);
|
||||
} else {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto &icon = st::stickersPremiumLock;
|
||||
const auto width = icon.width();
|
||||
const auto height = icon.height();
|
||||
const auto rect = QRect(
|
||||
QPoint(x + size - width, y + size - height),
|
||||
icon.size());
|
||||
const auto added = QMargins(add, add, add, add);
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
cache.badge = QImage(
|
||||
(rect + added).size() * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
cache.badge.setDevicePixelRatio(ratio);
|
||||
cache.badge.fill(Qt::transparent);
|
||||
const auto inner = QRect(add, add, rect.width(), rect.height());
|
||||
auto q = QPainter(&cache.badge);
|
||||
auto pen = check.border->p;
|
||||
pen.setWidthF(check.width);
|
||||
q.setPen(pen);
|
||||
q.setBrush(st::premiumButtonBg2);
|
||||
q.drawEllipse(inner);
|
||||
icon.paintInCenter(q, inner);
|
||||
}
|
||||
}
|
||||
const auto cached = cache.badge.size() / cache.badge.devicePixelRatio();
|
||||
const auto left = x + size + add - cached.width();
|
||||
const auto top = stars ? (y - add) : (y + size + add - cached.height());
|
||||
p.drawImage(left, top, cache.badge);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@ namespace style {
|
|||
struct PeerListItem;
|
||||
} // namespace style
|
||||
|
||||
namespace Api {
|
||||
struct MessageMoneyRestriction;
|
||||
} // namespace Api
|
||||
|
||||
namespace Data {
|
||||
class Thread;
|
||||
class Forum;
|
||||
|
@ -100,6 +104,21 @@ struct RecipientMoneyRestrictionError {
|
|||
[[nodiscard]] RecipientMoneyRestrictionError WriteMoneyRestrictionError(
|
||||
not_null<UserData*> user);
|
||||
|
||||
struct RestrictionBadgeCache {
|
||||
int paletteVersion = 0;
|
||||
int stars = 0;
|
||||
QImage badge;
|
||||
};
|
||||
void PaintRestrictionBadge(
|
||||
Painter &p,
|
||||
not_null<const style::PeerListItem*> st,
|
||||
int stars,
|
||||
RestrictionBadgeCache &cache,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size);
|
||||
|
||||
class RecipientRow : public PeerListRow {
|
||||
public:
|
||||
explicit RecipientRow(
|
||||
|
@ -117,21 +136,20 @@ public:
|
|||
[[nodiscard]] History *maybeHistory() const {
|
||||
return _maybeHistory;
|
||||
}
|
||||
[[nodiscard]] bool locked() const {
|
||||
return _lockedSt != nullptr;
|
||||
}
|
||||
void setLocked(const style::PeerListItem *lockedSt) {
|
||||
_lockedSt = lockedSt;
|
||||
}
|
||||
PaintRoundImageCallback generatePaintUserpicCallback(
|
||||
bool forceRound) override;
|
||||
|
||||
void preloadUserpic() override;
|
||||
|
||||
[[nodiscard]] Api::MessageMoneyRestriction restriction() const;
|
||||
void setRestriction(Api::MessageMoneyRestriction restriction);
|
||||
|
||||
private:
|
||||
struct Restriction;
|
||||
|
||||
History *_maybeHistory = nullptr;
|
||||
const style::PeerListItem *_lockedSt = nullptr;
|
||||
bool _resolvePremiumRequired = false;
|
||||
const style::PeerListItem *_maybeLockedSt = nullptr;
|
||||
std::shared_ptr<Restriction> _restriction;
|
||||
|
||||
};
|
||||
|
||||
|
@ -371,11 +389,3 @@ private:
|
|||
Fn<bool(not_null<Data::ForumTopic*>)> _filter;
|
||||
|
||||
};
|
||||
|
||||
void PaintPremiumRequiredLock(
|
||||
Painter &p,
|
||||
not_null<const style::PeerListItem*> st,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth,
|
||||
int size);
|
||||
|
|
|
@ -1486,6 +1486,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
|||
};
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
Fn<bool(int messages)> checkPaid,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions) {
|
||||
|
@ -1503,6 +1504,8 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
|||
result.size() > 1));
|
||||
}
|
||||
return;
|
||||
} else if (!checkPaid(1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
*sending = true;
|
||||
|
|
|
@ -118,12 +118,13 @@ private:
|
|||
Ui::RoundImageCheckbox checkbox;
|
||||
Ui::Text::String name;
|
||||
Ui::Animations::Simple nameActive;
|
||||
bool locked = false;
|
||||
Api::MessageMoneyRestriction restriction;
|
||||
RestrictionBadgeCache badgeCache;
|
||||
};
|
||||
|
||||
void invalidateCache();
|
||||
bool showLockedError(not_null<Chat*> chat);
|
||||
void refreshLockedRows();
|
||||
void refreshRestrictedRows();
|
||||
|
||||
[[nodiscard]] int displayedChatsCount() const;
|
||||
[[nodiscard]] not_null<Data::Thread*> chatThread(
|
||||
|
@ -132,7 +133,7 @@ private:
|
|||
void paintChat(Painter &p, not_null<Chat*> chat, int index);
|
||||
void updateChat(not_null<PeerData*> peer);
|
||||
void updateChatName(not_null<Chat*> chat);
|
||||
void initChatLocked(not_null<Chat*> chat);
|
||||
void initChatRestriction(not_null<Chat*> chat);
|
||||
void repaintChat(not_null<PeerData*> peer);
|
||||
int chatIndex(not_null<PeerData*> peer) const;
|
||||
void repaintChatAtIndex(int index);
|
||||
|
@ -652,6 +653,67 @@ void ShareBox::innerSelectedChanged(
|
|||
}
|
||||
|
||||
void ShareBox::submit(Api::SendOptions options) {
|
||||
_submitLifetime.destroy();
|
||||
|
||||
auto threads = _inner->selected();
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
const auto checkPaid = [=](int messagesCount) {
|
||||
const auto withPaymentApproved = crl::guard(weak, [=](int approved) {
|
||||
auto copy = options;
|
||||
copy.starsApproved = approved;
|
||||
submit(copy);
|
||||
});
|
||||
|
||||
const auto alreadyApproved = options.starsApproved;
|
||||
auto paid = std::vector<not_null<Data::Thread*>>();
|
||||
auto waiting = base::flat_set<not_null<PeerData*>>();
|
||||
auto totalStars = 0;
|
||||
for (const auto &thread : threads) {
|
||||
const auto peer = thread->peer();
|
||||
const auto details = ComputePaymentDetails(peer, messagesCount);
|
||||
if (!details) {
|
||||
waiting.emplace(peer);
|
||||
} else if (details->stars > 0) {
|
||||
totalStars += details->stars;
|
||||
paid.push_back(thread);
|
||||
}
|
||||
}
|
||||
if (!waiting.empty()) {
|
||||
_descriptor.session->changes().peerUpdates(
|
||||
Data::PeerUpdate::Flag::FullInfo
|
||||
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||
if (waiting.contains(update.peer)) {
|
||||
withPaymentApproved(alreadyApproved);
|
||||
}
|
||||
}, _submitLifetime);
|
||||
|
||||
if (!_descriptor.session->credits().loaded()) {
|
||||
_descriptor.session->credits().loadedValue(
|
||||
) | rpl::filter(
|
||||
rpl::mappers::_1
|
||||
) | rpl::take(1) | rpl::start_with_next([=] {
|
||||
withPaymentApproved(alreadyApproved);
|
||||
}, _submitLifetime);
|
||||
}
|
||||
return false;
|
||||
} else if (totalStars > alreadyApproved) {
|
||||
const auto show = uiShow();
|
||||
const auto session = _descriptor.session;
|
||||
const auto sessionShow = Main::MakeSessionShow(show, session);
|
||||
const auto scheduleBoxSt = _descriptor.st.scheduleBox.get();
|
||||
ShowSendPaidConfirm(sessionShow, paid, SendPaymentDetails{
|
||||
.messages = messagesCount,
|
||||
.stars = totalStars,
|
||||
}, [=] { withPaymentApproved(totalStars); }, PaidConfirmStyles{
|
||||
.label = (scheduleBoxSt
|
||||
? scheduleBoxSt->chooseDateTimeArgs.labelStyle
|
||||
: nullptr),
|
||||
.checkbox = _descriptor.st.checkbox,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (const auto onstack = _descriptor.submitCallback) {
|
||||
const auto forwardOptions = (_forwardOptions.captionsCount
|
||||
&& _forwardOptions.dropCaptions)
|
||||
|
@ -660,7 +722,8 @@ void ShareBox::submit(Api::SendOptions options) {
|
|||
? Data::ForwardOptions::NoSenderNames
|
||||
: Data::ForwardOptions::PreserveInfo;
|
||||
onstack(
|
||||
_inner->selected(),
|
||||
std::move(threads),
|
||||
checkPaid,
|
||||
_comment->entity()->getTextWithAppliedMarkdown(),
|
||||
options,
|
||||
forwardOptions);
|
||||
|
@ -727,7 +790,7 @@ ShareBox::Inner::Inner(
|
|||
Data::AmPremiumValue(session) | rpl::to_empty,
|
||||
session->api().premium().someMessageMoneyRestrictionsResolved()
|
||||
) | rpl::start_with_next([=] {
|
||||
refreshLockedRows();
|
||||
refreshRestrictedRows();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
|
@ -788,7 +851,7 @@ void ShareBox::Inner::invalidateCache() {
|
|||
}
|
||||
|
||||
bool ShareBox::Inner::showLockedError(not_null<Chat*> chat) {
|
||||
if (!chat->locked) {
|
||||
if (!chat->restriction.premiumRequired) {
|
||||
return false;
|
||||
}
|
||||
::Settings::ShowPremiumPromoToast(
|
||||
|
@ -799,25 +862,25 @@ bool ShareBox::Inner::showLockedError(not_null<Chat*> chat) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ShareBox::Inner::refreshLockedRows() {
|
||||
void ShareBox::Inner::refreshRestrictedRows() {
|
||||
auto changed = false;
|
||||
for (const auto &[peer, data] : _dataMap) {
|
||||
const auto history = data->history;
|
||||
const auto locked = Api::ResolveMessageMoneyRestrictions(
|
||||
const auto restriction = Api::ResolveMessageMoneyRestrictions(
|
||||
history->peer,
|
||||
history).premiumRequired;
|
||||
if (data->locked != locked) {
|
||||
data->locked = locked;
|
||||
history);
|
||||
if (data->restriction != restriction) {
|
||||
data->restriction = restriction;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
for (const auto &data : d_byUsernameFiltered) {
|
||||
const auto history = data->history;
|
||||
const auto locked = Api::ResolveMessageMoneyRestrictions(
|
||||
const auto restriction = Api::ResolveMessageMoneyRestrictions(
|
||||
history->peer,
|
||||
history).premiumRequired;
|
||||
if (data->locked != locked) {
|
||||
data->locked = locked;
|
||||
history);
|
||||
if (data->restriction != restriction) {
|
||||
data->restriction = restriction;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -884,13 +947,14 @@ void ShareBox::Inner::updateChatName(not_null<Chat*> chat) {
|
|||
chat->name.setText(_st.item.nameStyle, text, Ui::NameTextOptions());
|
||||
}
|
||||
|
||||
void ShareBox::Inner::initChatLocked(not_null<Chat*> chat) {
|
||||
void ShareBox::Inner::initChatRestriction(not_null<Chat*> chat) {
|
||||
if (_descriptor.moneyRestrictionError) {
|
||||
const auto history = chat->history;
|
||||
if (Api::ResolveMessageMoneyRestrictions(
|
||||
history->peer,
|
||||
history).premiumRequired) {
|
||||
chat->locked = true;
|
||||
const auto restriction = Api::ResolveMessageMoneyRestrictions(
|
||||
history->peer,
|
||||
history);
|
||||
if (restriction) {
|
||||
chat->restriction = restriction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1021,9 +1085,10 @@ void ShareBox::Inner::preloadUserpic(not_null<Dialogs::Entry*> entry) {
|
|||
} else if (!Api::ResolveMessageMoneyRestrictions(
|
||||
history->peer,
|
||||
history).known) {
|
||||
const auto user = history->peer->asUser();
|
||||
_descriptor.session->api().premium().resolveMessageMoneyRestrictions(
|
||||
user);
|
||||
if (const auto user = history->peer->asUser()) {
|
||||
const auto api = &_descriptor.session->api();
|
||||
api->premium().resolveMessageMoneyRestrictions(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1046,7 +1111,7 @@ auto ShareBox::Inner::getChat(not_null<Dialogs::Row*> row)
|
|||
repaintChat(peer);
|
||||
}));
|
||||
updateChatName(i->second.get());
|
||||
initChatLocked(i->second.get());
|
||||
initChatRestriction(i->second.get());
|
||||
row->attached = i->second.get();
|
||||
return i->second.get();
|
||||
}
|
||||
|
@ -1080,10 +1145,12 @@ void ShareBox::Inner::paintChat(
|
|||
auto photoTop = st::sharePhotoTop;
|
||||
chat->checkbox.paint(p, x + photoLeft, y + photoTop, outerWidth);
|
||||
|
||||
if (chat->locked) {
|
||||
PaintPremiumRequiredLock(
|
||||
if (chat->restriction) {
|
||||
PaintRestrictionBadge(
|
||||
p,
|
||||
&_st.item,
|
||||
chat->restriction.starsPerMessage,
|
||||
chat->badgeCache,
|
||||
x + photoLeft,
|
||||
y + photoTop,
|
||||
outerWidth,
|
||||
|
@ -1438,7 +1505,7 @@ void ShareBox::Inner::peopleReceived(
|
|||
_st.item,
|
||||
[=] { repaintChat(peer); }));
|
||||
updateChatName(d_byUsernameFiltered.back().get());
|
||||
initChatLocked(d_byUsernameFiltered.back().get());
|
||||
initChatRestriction(d_byUsernameFiltered.back().get());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1502,24 +1569,29 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
const auto state = std::make_shared<State>();
|
||||
return [=](
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Fn<bool(int messagesCount)> checkPaid,
|
||||
TextWithTags comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions forwardOptions) {
|
||||
if (!state->requests.empty()) {
|
||||
return; // Share clicked already.
|
||||
}
|
||||
|
||||
const auto items = history->owner().idsToItems(msgIds);
|
||||
const auto existingIds = history->owner().itemsToIds(items);
|
||||
if (existingIds.empty() || result.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto messagesCount = int(items.size()) + (comment.empty() ? 0 : 1);
|
||||
const auto error = GetErrorForSending(
|
||||
result,
|
||||
{ .forward = &items, .text = &comment });
|
||||
if (error.error) {
|
||||
show->showBox(MakeSendErrorBox(error, result.size() > 1));
|
||||
return;
|
||||
} else if (!checkPaid(messagesCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
using Flag = MTPmessages_ForwardMessages::Flag;
|
||||
|
@ -1614,7 +1686,11 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
}
|
||||
finish();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"VOICE_MESSAGES_FORBIDDEN"_q) {
|
||||
const auto type = error.type();
|
||||
if (type.startsWith(u"ALLOW_PAYMENT_REQUIRED_"_q)) {
|
||||
show->showToast(u"Payment requirements changed. "
|
||||
"Please, try again."_q);
|
||||
} else if (type == u"VOICE_MESSAGES_FORBIDDEN"_q) {
|
||||
show->showToast(
|
||||
tr::lng_restricted_send_voice_messages(
|
||||
tr::now,
|
||||
|
@ -1653,6 +1729,7 @@ ShareBoxStyleOverrides DarkShareBoxStyle() {
|
|||
.comment = &st::groupCallShareBoxComment,
|
||||
.peerList = &st::groupCallShareBoxList,
|
||||
.label = &st::groupCallField,
|
||||
.checkbox = &st::groupCallCheckbox,
|
||||
.scheduleBox = std::make_shared<ScheduleBoxStyleArgs>(schedule()),
|
||||
};
|
||||
}
|
||||
|
@ -1765,6 +1842,7 @@ void FastShareLink(
|
|||
};
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<::Data::Thread*>> &&result,
|
||||
Fn<bool(int messages)> checkPaid,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
::Data::ForwardOptions) {
|
||||
|
@ -1781,6 +1859,8 @@ void FastShareLink(
|
|||
MakeSendErrorBox(error, result.size() > 1));
|
||||
}
|
||||
return;
|
||||
} else if (!checkPaid(1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
*sending = true;
|
||||
|
|
|
@ -66,6 +66,7 @@ struct ShareBoxStyleOverrides {
|
|||
const style::InputField *comment = nullptr;
|
||||
const style::PeerList *peerList = nullptr;
|
||||
const style::InputField *label = nullptr;
|
||||
const style::Checkbox *checkbox = nullptr;
|
||||
std::shared_ptr<HistoryView::ScheduleBoxStyleArgs> scheduleBox;
|
||||
};
|
||||
[[nodiscard]] ShareBoxStyleOverrides DarkShareBoxStyle();
|
||||
|
@ -96,6 +97,7 @@ public:
|
|||
using CopyCallback = Fn<void()>;
|
||||
using SubmitCallback = Fn<void(
|
||||
std::vector<not_null<Data::Thread*>>&&,
|
||||
Fn<bool(int messages)> checkPaid,
|
||||
TextWithTags&&,
|
||||
Api::SendOptions,
|
||||
Data::ForwardOptions)>;
|
||||
|
@ -196,5 +198,6 @@ private:
|
|||
PeopleQueries _peopleQueries;
|
||||
|
||||
Ui::Animations::Simple _scrollAnimation;
|
||||
rpl::lifetime _submitLifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -134,6 +134,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
|
|||
};
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
Fn<bool(int messages)> checkPaid,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions) {
|
||||
|
@ -150,6 +151,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
|
|||
MakeSendErrorBox(error, result.size() > 1));
|
||||
}
|
||||
return;
|
||||
} else if (!checkPaid(1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
*sending = true;
|
||||
|
|
|
@ -1747,7 +1747,8 @@ void InitFieldAutocomplete(
|
|||
&& peer->isUser()
|
||||
&& !peer->asUser()->isBot()
|
||||
&& (!shortcutMessages
|
||||
|| shortcutMessages->shortcuts().list.empty())) {
|
||||
|| shortcutMessages->shortcuts().list.empty()
|
||||
|| peer->starsPerMessageChecked() != 0)) {
|
||||
parsed = {};
|
||||
}
|
||||
raw->showFiltered(peer, parsed.query, parsed.fromStart);
|
||||
|
|
|
@ -249,19 +249,42 @@ void ShowSendPaidConfirm(
|
|||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed) {
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles) {
|
||||
return ShowSendPaidConfirm(
|
||||
navigation->uiShow(),
|
||||
peer,
|
||||
details,
|
||||
confirmed);
|
||||
confirmed,
|
||||
styles);
|
||||
}
|
||||
|
||||
void ShowSendPaidConfirm(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<PeerData*> peer,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed) {
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles) {
|
||||
ShowSendPaidConfirm(
|
||||
std::move(show),
|
||||
std::vector<not_null<Data::Thread*>>{ peer->owner().history(peer) },
|
||||
details,
|
||||
confirmed,
|
||||
styles);
|
||||
}
|
||||
|
||||
void ShowSendPaidConfirm(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
const std::vector<not_null<Data::Thread*>> &threads,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles) {
|
||||
Expects(!threads.empty());
|
||||
|
||||
const auto singlePeer = (threads.size() > 1)
|
||||
? (PeerData*)nullptr
|
||||
: threads.front()->peer().get();
|
||||
const auto recipientId = singlePeer ? singlePeer->id : PeerId();
|
||||
const auto check = [=] {
|
||||
const auto required = details.stars;
|
||||
if (!required) {
|
||||
|
@ -276,57 +299,81 @@ void ShowSendPaidConfirm(
|
|||
Settings::MaybeRequestBalanceIncrease(
|
||||
show,
|
||||
required,
|
||||
Settings::SmallBalanceForMessage{ .recipientId = peer->id },
|
||||
Settings::SmallBalanceForMessage{ .recipientId = recipientId },
|
||||
done);
|
||||
};
|
||||
|
||||
const auto session = &peer->session();
|
||||
if (session->local().isPeerTrustedPayForMessage(peer->id)) {
|
||||
check();
|
||||
return;
|
||||
auto usersOnly = true;
|
||||
for (const auto &thread : threads) {
|
||||
if (!thread->peer()->isUser()) {
|
||||
usersOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (singlePeer) {
|
||||
const auto session = &singlePeer->session();
|
||||
if (session->local().isPeerTrustedPayForMessage(recipientId)) {
|
||||
check();
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto messages = details.messages;
|
||||
const auto stars = details.stars;
|
||||
show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto trust = std::make_shared<QPointer<Ui::Checkbox>>();
|
||||
const auto proceed = [=](Fn<void()> close) {
|
||||
if ((*trust)->checked()) {
|
||||
session->local().markPeerTrustedPayForMessage(peer->id);
|
||||
if (singlePeer && (*trust)->checked()) {
|
||||
const auto session = &singlePeer->session();
|
||||
session->local().markPeerTrustedPayForMessage(recipientId);
|
||||
}
|
||||
check();
|
||||
close();
|
||||
};
|
||||
Ui::ConfirmBox(box, {
|
||||
.text = tr::lng_payment_confirm_text(
|
||||
tr::now,
|
||||
lt_count,
|
||||
stars / messages,
|
||||
lt_name,
|
||||
Ui::Text::Bold(peer->shortName()),
|
||||
Ui::Text::RichLangValue).append(' ').append(
|
||||
tr::lng_payment_confirm_sure(
|
||||
.text = (singlePeer
|
||||
? tr::lng_payment_confirm_text(
|
||||
tr::now,
|
||||
lt_count,
|
||||
stars / messages,
|
||||
lt_name,
|
||||
Ui::Text::Bold(singlePeer->shortName()),
|
||||
Ui::Text::RichLangValue)
|
||||
: (usersOnly
|
||||
? tr::lng_payment_confirm_users
|
||||
: tr::lng_payment_confirm_chats)(
|
||||
tr::now,
|
||||
lt_count,
|
||||
messages,
|
||||
lt_amount,
|
||||
tr::lng_payment_confirm_amount(
|
||||
tr::now,
|
||||
lt_count,
|
||||
stars,
|
||||
Ui::Text::RichLangValue),
|
||||
Ui::Text::RichLangValue)),
|
||||
int(threads.size()),
|
||||
Ui::Text::RichLangValue)).append(' ').append(
|
||||
tr::lng_payment_confirm_sure(
|
||||
tr::now,
|
||||
lt_count,
|
||||
messages,
|
||||
lt_amount,
|
||||
tr::lng_payment_confirm_amount(
|
||||
tr::now,
|
||||
lt_count,
|
||||
stars,
|
||||
Ui::Text::RichLangValue),
|
||||
Ui::Text::RichLangValue)),
|
||||
.confirmed = proceed,
|
||||
.confirmText = tr::lng_payment_confirm_button(
|
||||
lt_count,
|
||||
rpl::single(messages * 1.)),
|
||||
.labelStyle = styles.label,
|
||||
.title = tr::lng_payment_confirm_title(),
|
||||
});
|
||||
const auto skip = st::defaultCheckbox.margin.top();
|
||||
*trust = box->addRow(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
box,
|
||||
tr::lng_payment_confirm_dont_ask(tr::now)),
|
||||
st::boxRowPadding + QMargins(0, skip, 0, skip));
|
||||
if (singlePeer) {
|
||||
const auto skip = st::defaultCheckbox.margin.top();
|
||||
*trust = box->addRow(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
box,
|
||||
tr::lng_payment_confirm_dont_ask(tr::now),
|
||||
false,
|
||||
(styles.checkbox
|
||||
? *styles.checkbox
|
||||
: st::defaultCheckbox)),
|
||||
st::boxRowPadding + QMargins(0, skip, 0, skip));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class History;
|
||||
|
||||
namespace style {
|
||||
struct FlatLabel;
|
||||
struct Checkbox;
|
||||
} // namespace style
|
||||
|
||||
namespace Api {
|
||||
struct SendOptions;
|
||||
struct SendAction;
|
||||
|
@ -144,16 +149,28 @@ struct SendPaymentDetails {
|
|||
not_null<PeerData*> peer,
|
||||
int messagesCount);
|
||||
|
||||
struct PaidConfirmStyles {
|
||||
const style::FlatLabel *label = nullptr;
|
||||
const style::Checkbox *checkbox = nullptr;
|
||||
};
|
||||
void ShowSendPaidConfirm(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed);
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles = {});
|
||||
void ShowSendPaidConfirm(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<PeerData*> peer,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed);
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles = {});
|
||||
void ShowSendPaidConfirm(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
const std::vector<not_null<Data::Thread*>> &threads,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles = {});
|
||||
|
||||
class SendPaymentHelper final {
|
||||
public:
|
||||
|
|
|
@ -4575,26 +4575,6 @@ void HistoryWidget::reportSelectedMessages() {
|
|||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::payForMessageSure(bool trust) {
|
||||
const auto required = _peer->starsPerMessage();
|
||||
if (!required) {
|
||||
return;
|
||||
}
|
||||
const auto done = [=](Settings::SmallBalanceResult result) {
|
||||
if (result == Settings::SmallBalanceResult::Success
|
||||
|| result == Settings::SmallBalanceResult::Already) {
|
||||
if (canWriteMessage()) {
|
||||
setInnerFocus();
|
||||
}
|
||||
}
|
||||
};
|
||||
Settings::MaybeRequestBalanceIncrease(
|
||||
controller()->uiShow(),
|
||||
required,
|
||||
Settings::SmallBalanceForMessage{ .recipientId = _peer->id },
|
||||
crl::guard(this, done));
|
||||
}
|
||||
|
||||
History *HistoryWidget::history() const {
|
||||
return _history;
|
||||
}
|
||||
|
|
|
@ -426,7 +426,6 @@ private:
|
|||
[[nodiscard]] int computeMaxFieldHeight() const;
|
||||
void toggleMuteUnmute();
|
||||
void reportSelectedMessages();
|
||||
void payForMessageSure(bool trust = false);
|
||||
void showKeyboardHideButton();
|
||||
void toggleKeyboard(bool manual = true);
|
||||
void startBotCommand();
|
||||
|
|
|
@ -78,6 +78,7 @@ namespace Media::Stories {
|
|||
: Fn<void()>();
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
Fn<bool(int messages)> checkPaid,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions forwardOptions) {
|
||||
|
@ -95,6 +96,8 @@ namespace Media::Stories {
|
|||
if (error.error) {
|
||||
show->showBox(MakeSendErrorBox(error, result.size() > 1));
|
||||
return;
|
||||
} else if (!checkPaid(comment.text.isEmpty() ? 1 : 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto api = &story->owner().session().api();
|
||||
|
@ -133,7 +136,7 @@ namespace Media::Stories {
|
|||
sendFlags |= SendFlag::f_invert_media;
|
||||
}
|
||||
const auto starsPaid = std::min(
|
||||
peer->starsPerMessageChecked(),
|
||||
threadHistory->peer->starsPerMessageChecked(),
|
||||
options.starsApproved);
|
||||
if (starsPaid) {
|
||||
options.starsApproved -= starsPaid;
|
||||
|
|
|
@ -147,53 +147,11 @@ void PaidReactionSlider(
|
|||
}
|
||||
|
||||
[[nodiscard]] QImage GenerateBadgeImage(int count) {
|
||||
const auto text = Lang::FormatCountDecimal(count);
|
||||
const auto length = st::chatSimilarBadgeFont->width(text);
|
||||
const auto contents = st::chatSimilarLockedIconPosition.x()
|
||||
+ st::paidReactTopStarIcon.width()
|
||||
+ st::paidReactTopStarSkip
|
||||
+ length;
|
||||
const auto badge = QRect(
|
||||
st::chatSimilarBadgePadding.left(),
|
||||
st::chatSimilarBadgePadding.top(),
|
||||
contents,
|
||||
st::chatSimilarBadgeFont->height);
|
||||
const auto rect = badge.marginsAdded(st::chatSimilarBadgePadding);
|
||||
|
||||
auto result = QImage(
|
||||
rect.size() * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
result.fill(Qt::transparent);
|
||||
auto q = QPainter(&result);
|
||||
|
||||
const auto &font = st::chatSimilarBadgeFont;
|
||||
const auto textTop = badge.y() + font->ascent;
|
||||
const auto icon = &st::paidReactTopStarIcon;
|
||||
const auto position = st::chatSimilarLockedIconPosition;
|
||||
|
||||
auto hq = PainterHighQualityEnabler(q);
|
||||
q.setBrush(st::creditsBg3);
|
||||
q.setPen(Qt::NoPen);
|
||||
const auto radius = rect.height() / 2.;
|
||||
q.drawRoundedRect(rect, radius, radius);
|
||||
|
||||
auto textLeft = 0;
|
||||
if (icon) {
|
||||
icon->paint(
|
||||
q,
|
||||
badge.x() + position.x(),
|
||||
badge.y() + position.y(),
|
||||
rect.width());
|
||||
textLeft += position.x() + icon->width() + st::paidReactTopStarSkip;
|
||||
}
|
||||
|
||||
q.setFont(font);
|
||||
q.setPen(st::premiumButtonFg);
|
||||
q.drawText(textLeft, textTop, text);
|
||||
q.end();
|
||||
|
||||
return result;
|
||||
return GenerateSmallBadgeImage(
|
||||
Lang::FormatCountDecimal(count),
|
||||
st::paidReactTopStarIcon,
|
||||
st::creditsBg3->c,
|
||||
st::premiumButtonFg->c);
|
||||
}
|
||||
|
||||
void AddArrowDown(not_null<RpWidget*> widget) {
|
||||
|
@ -321,7 +279,6 @@ void SelectShownPeer(
|
|||
updateUserpic();
|
||||
}
|
||||
(*menu)->popup(QCursor::pos());
|
||||
|
||||
}
|
||||
|
||||
void FillTopReactors(
|
||||
|
@ -633,4 +590,65 @@ object_ptr<BoxContent> MakePaidReactionBox(PaidReactionBoxArgs &&args) {
|
|||
return Box(PaidReactionsBox, std::move(args));
|
||||
}
|
||||
|
||||
QImage GenerateSmallBadgeImage(
|
||||
QString text,
|
||||
const style::icon &icon,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
const style::RoundCheckbox *borderSt) {
|
||||
const auto length = st::chatSimilarBadgeFont->width(text);
|
||||
const auto contents = st::chatSimilarLockedIconPosition.x()
|
||||
+ icon.width()
|
||||
+ st::paidReactTopStarSkip
|
||||
+ length;
|
||||
const auto badge = QRect(
|
||||
st::chatSimilarBadgePadding.left(),
|
||||
st::chatSimilarBadgePadding.top(),
|
||||
contents,
|
||||
st::chatSimilarBadgeFont->height);
|
||||
const auto rect = badge.marginsAdded(st::chatSimilarBadgePadding);
|
||||
const auto add = borderSt ? borderSt->width : 0;
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto result = QImage(
|
||||
(rect + QMargins(add, add, add, add)).size() * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(ratio);
|
||||
result.fill(Qt::transparent);
|
||||
auto q = QPainter(&result);
|
||||
|
||||
const auto &font = st::chatSimilarBadgeFont;
|
||||
const auto textTop = badge.y() + font->ascent;
|
||||
const auto position = st::chatSimilarLockedIconPosition;
|
||||
|
||||
auto hq = PainterHighQualityEnabler(q);
|
||||
q.translate(add, add);
|
||||
q.setBrush(bg);
|
||||
if (borderSt) {
|
||||
q.setPen(QPen(borderSt->border->c, borderSt->width));
|
||||
} else {
|
||||
q.setPen(Qt::NoPen);
|
||||
}
|
||||
const auto radius = rect.height() / 2.;
|
||||
const auto shift = add / 2.;
|
||||
q.drawRoundedRect(
|
||||
QRectF(rect) + QMarginsF(shift, shift, shift, shift),
|
||||
radius,
|
||||
radius);
|
||||
|
||||
auto textLeft = 0;
|
||||
icon.paint(
|
||||
q,
|
||||
badge.x() + position.x(),
|
||||
badge.y() + position.y(),
|
||||
rect.width());
|
||||
textLeft += position.x() + icon.width() + st::paidReactTopStarSkip;
|
||||
|
||||
q.setFont(font);
|
||||
q.setPen(fg);
|
||||
q.drawText(textLeft, textTop, text);
|
||||
q.end();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
namespace style {
|
||||
struct RoundCheckbox;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class BoxContent;
|
||||
|
@ -48,4 +52,11 @@ void PaidReactionsBox(
|
|||
[[nodiscard]] object_ptr<BoxContent> MakePaidReactionBox(
|
||||
PaidReactionBoxArgs &&args);
|
||||
|
||||
[[nodiscard]] QImage GenerateSmallBadgeImage(
|
||||
QString text,
|
||||
const style::icon &icon,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
const style::RoundCheckbox *borderSt = nullptr);
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -2091,7 +2091,9 @@ void SmallBalanceBox(
|
|||
}, [&](SmallBalanceStarGift value) {
|
||||
return owner->peer(value.recipientId)->shortName();
|
||||
}, [&](SmallBalanceForMessage value) {
|
||||
return owner->peer(value.recipientId)->shortName();
|
||||
return value.recipientId
|
||||
? owner->peer(value.recipientId)->shortName()
|
||||
: QString();
|
||||
});
|
||||
|
||||
auto needed = show->session().credits().balanceValue(
|
||||
|
@ -2131,10 +2133,13 @@ void SmallBalanceBox(
|
|||
rpl::single(Ui::Text::Bold(name)),
|
||||
Ui::Text::RichLangValue)
|
||||
: v::is<SmallBalanceForMessage>(source)
|
||||
? tr::lng_credits_small_balance_for_message(
|
||||
lt_user,
|
||||
rpl::single(Ui::Text::Bold(name)),
|
||||
Ui::Text::RichLangValue)
|
||||
? (name.isEmpty()
|
||||
? tr::lng_credits_small_balance_for_messages(
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_credits_small_balance_for_message(
|
||||
lt_user,
|
||||
rpl::single(Ui::Text::Bold(name)),
|
||||
Ui::Text::RichLangValue))
|
||||
: name.isEmpty()
|
||||
? tr::lng_credits_small_balance_fallback(
|
||||
Ui::Text::RichLangValue)
|
||||
|
|
|
@ -2409,6 +2409,7 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
not_null<PeerData*> peer) -> Controller::Chosen {
|
||||
return peer->owner().history(peer);
|
||||
}) | ranges::to_vector,
|
||||
[](int messagesCount) { return true; },
|
||||
comment->entity()->getTextWithAppliedMarkdown(),
|
||||
options,
|
||||
state->box->forwardOptionsData());
|
||||
|
|
Loading…
Add table
Reference in a new issue