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