init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
This commit is contained in:
309
Telegram/SourceFiles/payments/payments_reaction_process.cpp
Normal file
309
Telegram/SourceFiles/payments/payments_reaction_process.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "payments/payments_reaction_process.h"
|
||||
|
||||
#include "api/api_credits.h"
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "apiwrap.h"
|
||||
#include "boxes/peers/prepare_short_info_box.h"
|
||||
#include "boxes/send_credits_box.h" // CreditsEmojiSmall.
|
||||
#include "calls/group/calls_group_call.h"
|
||||
#include "calls/group/calls_group_messages.h"
|
||||
#include "core/ui_integration.h" // TextContext.
|
||||
#include "data/components/credits.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/session/session_show.h"
|
||||
#include "main/session/send_as_peers.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "payments/ui/payments_reaction_box.h"
|
||||
#include "settings/settings_credits_graphics.h"
|
||||
#include "ui/effects/reaction_fly_animation.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/layers/show.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "window/window_session_controller.h"
|
||||
|
||||
namespace Payments {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxPerReactionFallback = 10'000;
|
||||
constexpr auto kDefaultPerReaction = 50;
|
||||
|
||||
void TryAddingPaidReaction(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
FullMsgId itemId,
|
||||
base::weak_ptr<HistoryView::Element> weakView,
|
||||
int count,
|
||||
std::optional<PeerId> shownPeer,
|
||||
Fn<void(bool)> finished) {
|
||||
const auto owner = &show->session().data();
|
||||
const auto checkItem = [=] {
|
||||
const auto item = owner->message(itemId);
|
||||
if (!item) {
|
||||
if (const auto onstack = finished) {
|
||||
onstack(false);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
};
|
||||
|
||||
const auto item = checkItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const auto done = [=](Settings::SmallBalanceResult result) {
|
||||
if (result == Settings::SmallBalanceResult::Success
|
||||
|| result == Settings::SmallBalanceResult::Already) {
|
||||
if (const auto item = checkItem()) {
|
||||
item->addPaidReaction(count, shownPeer);
|
||||
if (const auto view = count ? weakView.get() : nullptr) {
|
||||
const auto history = view->history();
|
||||
history->owner().notifyViewPaidReactionSent(view);
|
||||
view->animateReaction({
|
||||
.id = Data::ReactionId::Paid(),
|
||||
});
|
||||
}
|
||||
if (const auto onstack = finished) {
|
||||
onstack(true);
|
||||
}
|
||||
}
|
||||
} else if (const auto onstack = finished) {
|
||||
onstack(false);
|
||||
}
|
||||
};
|
||||
const auto channelId = peerToChannel(itemId.peer);
|
||||
Settings::MaybeRequestBalanceIncrease(
|
||||
show,
|
||||
count,
|
||||
Settings::SmallBalanceReaction{ .channelId = channelId },
|
||||
done);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool LookupMyPaidAnonymous(not_null<HistoryItem*> item) {
|
||||
for (const auto &entry : item->topPaidReactionsWithLocal()) {
|
||||
if (entry.my) {
|
||||
return !entry.peer;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TryAddingPaidReaction(
|
||||
not_null<HistoryItem*> item,
|
||||
HistoryView::Element *view,
|
||||
int count,
|
||||
std::optional<PeerId> shownPeer,
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
Fn<void(bool)> finished) {
|
||||
TryAddingPaidReaction(
|
||||
std::move(show),
|
||||
item->fullId(),
|
||||
view,
|
||||
count,
|
||||
shownPeer,
|
||||
std::move(finished));
|
||||
}
|
||||
|
||||
void TryAddingPaidReaction(
|
||||
not_null<Calls::GroupCall*> call,
|
||||
int count,
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
Fn<void(bool)> finished) {
|
||||
const auto checkCall = [weak = base::make_weak(call), finished] {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
if (const auto onstack = finished) {
|
||||
onstack(false);
|
||||
}
|
||||
}
|
||||
return strong;
|
||||
};
|
||||
|
||||
const auto done = [=](Settings::SmallBalanceResult result) {
|
||||
if (result == Settings::SmallBalanceResult::Success
|
||||
|| result == Settings::SmallBalanceResult::Already) {
|
||||
if (const auto call = checkCall()) {
|
||||
call->messages()->reactionsPaidAdd(count);
|
||||
call->peer()->owner().notifyCallPaidReactionSent(call);
|
||||
if (const auto onstack = finished) {
|
||||
onstack(true);
|
||||
}
|
||||
}
|
||||
} else if (const auto onstack = finished) {
|
||||
onstack(false);
|
||||
}
|
||||
};
|
||||
Settings::MaybeRequestBalanceIncrease(
|
||||
show,
|
||||
count,
|
||||
Settings::SmallBalanceVideoStream{ .streamerId = call->peer()->id },
|
||||
done);
|
||||
}
|
||||
|
||||
void ShowPaidReactionDetails(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item,
|
||||
HistoryView::Element *view,
|
||||
HistoryReactionSource source) {
|
||||
Expects(item->history()->peer->isBroadcast()
|
||||
|| item->isDiscussionPost());
|
||||
|
||||
const auto show = controller->uiShow();
|
||||
const auto itemId = item->fullId();
|
||||
const auto session = &item->history()->session();
|
||||
const auto appConfig = &session->appConfig();
|
||||
|
||||
const auto max = std::max(
|
||||
appConfig->get<int>(
|
||||
u"stars_paid_reaction_amount_max"_q,
|
||||
kMaxPerReactionFallback),
|
||||
2);
|
||||
const auto chosen = std::clamp(kDefaultPerReaction, 1, max);
|
||||
|
||||
struct State {
|
||||
base::weak_qptr<Ui::BoxContent> selectBox;
|
||||
bool ignoreShownPeerSwitch = false;
|
||||
bool sending = false;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
session->credits().load(true);
|
||||
|
||||
const auto weakView = base::make_weak(view);
|
||||
const auto send = [=](int count, PeerId shownPeer, auto resend) -> void {
|
||||
Expects(count >= 0);
|
||||
|
||||
const auto finish = [=](bool success) {
|
||||
state->sending = false;
|
||||
if (success && count > 0) {
|
||||
state->ignoreShownPeerSwitch = true;
|
||||
if (const auto strong = state->selectBox.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}
|
||||
};
|
||||
if (state->sending || (!count && state->ignoreShownPeerSwitch)) {
|
||||
return;
|
||||
} else if (const auto item = session->data().message(itemId)) {
|
||||
state->sending = true;
|
||||
TryAddingPaidReaction(
|
||||
item,
|
||||
weakView.get(),
|
||||
count,
|
||||
shownPeer,
|
||||
show,
|
||||
finish);
|
||||
}
|
||||
};
|
||||
|
||||
auto submitText = [=](rpl::producer<int> amount) {
|
||||
auto nice = std::move(amount) | rpl::map([=](int count) {
|
||||
return Ui::CreditsEmojiSmall().append(
|
||||
Lang::FormatCountDecimal(count));
|
||||
});
|
||||
return tr::lng_paid_react_send(
|
||||
lt_price,
|
||||
std::move(nice),
|
||||
tr::rich);
|
||||
};
|
||||
auto top = std::vector<Ui::PaidReactionTop>();
|
||||
const auto add = [&](const Data::MessageReactionsTopPaid &entry) {
|
||||
const auto peer = entry.peer;
|
||||
const auto name = peer
|
||||
? peer->shortName()
|
||||
: tr::lng_paid_react_anonymous(tr::now);
|
||||
const auto open = [=] {
|
||||
controller->uiShow()->show(PrepareShortInfoBox(peer, controller));
|
||||
};
|
||||
top.push_back({
|
||||
.name = name,
|
||||
.photo = (peer
|
||||
? Ui::MakeUserpicThumbnail(peer)
|
||||
: Ui::MakeHiddenAuthorThumbnail()),
|
||||
.barePeerId = peer ? uint64(peer->id.value) : 0,
|
||||
.count = int(entry.count),
|
||||
.click = peer ? open : Fn<void()>(),
|
||||
.my = (entry.my == 1),
|
||||
});
|
||||
};
|
||||
const auto linked = item->discussionPostOriginalSender();
|
||||
const auto channel = (linked ? linked : item->history()->peer.get());
|
||||
const auto channels = session->sendAsPeers().list(
|
||||
{ channel, Main::SendAsType::PaidReaction }
|
||||
) | ranges::views::transform(
|
||||
&Main::SendAsPeer::peer
|
||||
) | ranges::to_vector;
|
||||
const auto topPaid = item->topPaidReactionsWithLocal();
|
||||
top.reserve(topPaid.size() + 2 + channels.size());
|
||||
for (const auto &entry : topPaid) {
|
||||
add(entry);
|
||||
}
|
||||
auto myAdded = base::flat_set<uint64>();
|
||||
const auto i = ranges::find(top, true, &Ui::PaidReactionTop::my);
|
||||
if (i != end(top)) {
|
||||
myAdded.emplace(i->barePeerId);
|
||||
}
|
||||
const auto myCount = uint32((i != end(top)) ? i->count : 0);
|
||||
const auto myAdd = [&](PeerData *peer) {
|
||||
const auto barePeerId = peer ? uint64(peer->id.value) : 0;
|
||||
if (!myAdded.emplace(barePeerId).second) {
|
||||
return;
|
||||
}
|
||||
add(Data::MessageReactionsTopPaid{
|
||||
.peer = peer,
|
||||
.count = myCount,
|
||||
.my = true,
|
||||
});
|
||||
};
|
||||
const auto globalPrivacy = &session->api().globalPrivacy();
|
||||
const auto shown = globalPrivacy->paidReactionShownPeerCurrent();
|
||||
const auto owner = &session->data();
|
||||
const auto shownPeer = shown ? owner->peer(shown).get() : nullptr;
|
||||
myAdd(shownPeer);
|
||||
myAdd(session->user());
|
||||
myAdd(nullptr);
|
||||
for (const auto &channel : channels) {
|
||||
myAdd(channel);
|
||||
}
|
||||
ranges::stable_sort(top, ranges::greater(), &Ui::PaidReactionTop::count);
|
||||
|
||||
state->selectBox = show->show(Ui::MakePaidReactionBox({
|
||||
.chosen = chosen,
|
||||
.max = max,
|
||||
.top = std::move(top),
|
||||
.session = &channel->session(),
|
||||
.name = channel->name(),
|
||||
.submit = std::move(submitText),
|
||||
.balanceValue = session->credits().balanceValue(),
|
||||
.send = [=](int count, uint64 barePeerId) {
|
||||
send(count, PeerId(barePeerId), send);
|
||||
},
|
||||
}));
|
||||
|
||||
if (const auto strong = state->selectBox.get()) {
|
||||
session->data().itemRemoved(
|
||||
) | rpl::on_next([=](not_null<const HistoryItem*> removed) {
|
||||
if (removed == item) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}, strong->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Payments
|
||||
Reference in New Issue
Block a user