mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 05:07:10 +02:00
Improve paid peer-box multi-send.
This commit is contained in:
parent
ee9d0cfd99
commit
fe2df96953
11 changed files with 226 additions and 46 deletions
|
@ -873,6 +873,7 @@ void PeerListRow::paintUserpic(
|
|||
} else if (const auto callback = generatePaintUserpicCallback(false)) {
|
||||
callback(p, x, y, outerWidth, st.photoSize);
|
||||
}
|
||||
paintUserpicOverlay(p, st, x, y, outerWidth);
|
||||
}
|
||||
|
||||
// Emulates Ui::RoundImageCheckbox::paint() in a checked state.
|
||||
|
|
|
@ -95,6 +95,13 @@ public:
|
|||
[[nodiscard]] virtual QString generateShortName();
|
||||
[[nodiscard]] virtual auto generatePaintUserpicCallback(
|
||||
bool forceRound) -> PaintRoundImageCallback;
|
||||
virtual void paintUserpicOverlay(
|
||||
Painter &p,
|
||||
const style::PeerListItem &st,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth) {
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual auto generateNameFirstLetters() const
|
||||
-> const base::flat_set<QChar> &;
|
||||
|
|
|
@ -311,24 +311,23 @@ void RecipientRow::setRestriction(Api::MessageMoneyRestriction restriction) {
|
|||
_restriction->value = restriction;
|
||||
}
|
||||
|
||||
PaintRoundImageCallback RecipientRow::generatePaintUserpicCallback(
|
||||
bool forceRound) {
|
||||
auto result = PeerListRow::generatePaintUserpicCallback(forceRound);
|
||||
void RecipientRow::paintUserpicOverlay(
|
||||
Painter &p,
|
||||
const style::PeerListItem &st,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth) {
|
||||
if (const auto &r = _restriction) {
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
result(p, x, y, outerWidth, size);
|
||||
PaintRestrictionBadge(
|
||||
p,
|
||||
_maybeLockedSt,
|
||||
r->value.starsPerMessage,
|
||||
r->cache,
|
||||
x,
|
||||
y,
|
||||
outerWidth,
|
||||
size);
|
||||
};
|
||||
PaintRestrictionBadge(
|
||||
p,
|
||||
_maybeLockedSt,
|
||||
r->value.starsPerMessage,
|
||||
r->cache,
|
||||
x,
|
||||
y,
|
||||
outerWidth,
|
||||
st.photoSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RecipientRow::refreshLock(
|
||||
|
|
|
@ -136,8 +136,12 @@ public:
|
|||
[[nodiscard]] History *maybeHistory() const {
|
||||
return _maybeHistory;
|
||||
}
|
||||
PaintRoundImageCallback generatePaintUserpicCallback(
|
||||
bool forceRound) override;
|
||||
void paintUserpicOverlay(
|
||||
Painter &p,
|
||||
const style::PeerListItem &st,
|
||||
int x,
|
||||
int y,
|
||||
int outerWidth) override;
|
||||
|
||||
void preloadUserpic() override;
|
||||
|
||||
|
|
|
@ -675,7 +675,7 @@ void ShareBox::submit(Api::SendOptions options) {
|
|||
});
|
||||
|
||||
const auto alreadyApproved = options.starsApproved;
|
||||
auto paid = std::vector<not_null<Data::Thread*>>();
|
||||
auto paid = std::vector<not_null<PeerData*>>();
|
||||
auto waiting = base::flat_set<not_null<PeerData*>>();
|
||||
auto totalStars = 0;
|
||||
for (const auto &thread : threads) {
|
||||
|
@ -685,7 +685,7 @@ void ShareBox::submit(Api::SendOptions options) {
|
|||
waiting.emplace(peer);
|
||||
} else if (details->stars > 0) {
|
||||
totalStars += details->stars;
|
||||
paid.push_back(thread);
|
||||
paid.push_back(peer);
|
||||
}
|
||||
}
|
||||
if (!waiting.empty()) {
|
||||
|
@ -963,7 +963,7 @@ void ShareBox::Inner::initChatRestriction(not_null<Chat*> chat) {
|
|||
const auto restriction = Api::ResolveMessageMoneyRestrictions(
|
||||
history->peer,
|
||||
history);
|
||||
if (restriction) {
|
||||
if (restriction || restriction.known) {
|
||||
chat->restriction = restriction;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ void ShowSendPaidConfirm(
|
|||
PaidConfirmStyles styles) {
|
||||
ShowSendPaidConfirm(
|
||||
std::move(show),
|
||||
std::vector<not_null<Data::Thread*>>{ peer->owner().history(peer) },
|
||||
std::vector<not_null<PeerData*>>{ peer },
|
||||
details,
|
||||
confirmed,
|
||||
styles);
|
||||
|
@ -275,15 +275,15 @@ void ShowSendPaidConfirm(
|
|||
|
||||
void ShowSendPaidConfirm(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
const std::vector<not_null<Data::Thread*>> &threads,
|
||||
const std::vector<not_null<PeerData*>> &peers,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles) {
|
||||
Expects(!threads.empty());
|
||||
Expects(!peers.empty());
|
||||
|
||||
const auto singlePeer = (threads.size() > 1)
|
||||
const auto singlePeer = (peers.size() > 1)
|
||||
? (PeerData*)nullptr
|
||||
: threads.front()->peer().get();
|
||||
: peers.front().get();
|
||||
const auto recipientId = singlePeer ? singlePeer->id : PeerId();
|
||||
const auto check = [=] {
|
||||
const auto required = details.stars;
|
||||
|
@ -303,8 +303,8 @@ void ShowSendPaidConfirm(
|
|||
done);
|
||||
};
|
||||
auto usersOnly = true;
|
||||
for (const auto &thread : threads) {
|
||||
if (!thread->peer()->isUser()) {
|
||||
for (const auto &peer : peers) {
|
||||
if (!peer->isUser()) {
|
||||
usersOnly = false;
|
||||
break;
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ void ShowSendPaidConfirm(
|
|||
: tr::lng_payment_confirm_chats)(
|
||||
tr::now,
|
||||
lt_count,
|
||||
int(threads.size()),
|
||||
int(peers.size()),
|
||||
Ui::Text::RichLangValue)).append(' ').append(
|
||||
tr::lng_payment_confirm_sure(
|
||||
tr::now,
|
||||
|
|
|
@ -167,7 +167,7 @@ void ShowSendPaidConfirm(
|
|||
PaidConfirmStyles styles = {});
|
||||
void ShowSendPaidConfirm(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
const std::vector<not_null<Data::Thread*>> &threads,
|
||||
const std::vector<not_null<PeerData*>> &peers,
|
||||
SendPaymentDetails details,
|
||||
Fn<void()> confirmed,
|
||||
PaidConfirmStyles styles = {});
|
||||
|
|
|
@ -436,7 +436,7 @@ void BottomInfo::layoutDateText() {
|
|||
auto marked = TextWithEntities{ full };
|
||||
if (const auto count = _data.stars) {
|
||||
marked.append(Ui::Text::IconEmoji(&st::starIconEmoji));
|
||||
marked.append(QString::number(count));
|
||||
marked.append(Lang::FormatCountToShort(count).string);
|
||||
}
|
||||
_authorEditedDate.setMarkedText(
|
||||
st::msgDateTextStyle,
|
||||
|
|
|
@ -1809,11 +1809,15 @@ void WebViewInstance::botSendPreparedMessage(
|
|||
QPointer<Ui::BoxContent> preview;
|
||||
QPointer<Ui::BoxContent> choose;
|
||||
rpl::event_stream<not_null<Data::Thread*>> recipient;
|
||||
Fn<void(Api::SendOptions)> send;
|
||||
SendPaymentHelper sendPayment;
|
||||
bool sent = false;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
auto recipient = state->recipient.events();
|
||||
const auto send = [=](std::vector<not_null<Data::Thread*>> list) {
|
||||
const auto send = [=](
|
||||
std::vector<not_null<Data::Thread*>> list,
|
||||
Api::SendOptions options) {
|
||||
if (state->sent) {
|
||||
return;
|
||||
}
|
||||
|
@ -1852,7 +1856,7 @@ void WebViewInstance::botSendPreparedMessage(
|
|||
bot->session().api().sendInlineResult(
|
||||
bot,
|
||||
parsed.get(),
|
||||
Api::SendAction(thread),
|
||||
Api::SendAction(thread, options),
|
||||
std::nullopt,
|
||||
done);
|
||||
}
|
||||
|
@ -1881,7 +1885,33 @@ void WebViewInstance::botSendPreparedMessage(
|
|||
state->choose = box.data();
|
||||
panel->showBox(std::move(box));
|
||||
}, [=](not_null<Data::Thread*> thread) {
|
||||
send({ thread });
|
||||
const auto weak = base::make_weak(thread);
|
||||
state->send = [=](Api::SendOptions options) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
state->send = nullptr;
|
||||
return;
|
||||
}
|
||||
const auto withPaymentApproved = [=](int stars) {
|
||||
if (const auto onstack = state->send) {
|
||||
auto copy = options;
|
||||
copy.starsApproved = stars;
|
||||
onstack(copy);
|
||||
}
|
||||
};
|
||||
const auto checked = state->sendPayment.check(
|
||||
uiShow(),
|
||||
strong->peer(),
|
||||
1,
|
||||
options.starsApproved,
|
||||
withPaymentApproved);
|
||||
if (!checked) {
|
||||
return;
|
||||
}
|
||||
state->send = nullptr;
|
||||
send({ strong }, options);
|
||||
};
|
||||
state->send({});
|
||||
});
|
||||
box->boxClosing() | rpl::start_with_next([=] {
|
||||
if (!state->sent) {
|
||||
|
|
|
@ -1960,7 +1960,9 @@ object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
|||
rpl::producer<QString> titleOverride,
|
||||
FnMut<void()> &&successCallback,
|
||||
InlineBots::PeerTypes typesRestriction,
|
||||
Fn<void(std::vector<not_null<Data::Thread*>>)> sendMany) {
|
||||
Fn<void(
|
||||
std::vector<not_null<Data::Thread*>>,
|
||||
Api::SendOptions)> sendMany) {
|
||||
const auto weak = std::make_shared<QPointer<PeerListBox>>();
|
||||
const auto selectable = (sendMany != nullptr);
|
||||
class Controller final : public ChooseRecipientBoxController {
|
||||
|
@ -2073,19 +2075,82 @@ object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
|||
std::move(filter),
|
||||
selectable);
|
||||
const auto raw = controller.get();
|
||||
|
||||
struct State {
|
||||
Fn<void(Api::SendOptions)> submit;
|
||||
rpl::lifetime submitLifetime;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
raw->hasSelectedChanges(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
box->clearButtons();
|
||||
if (shown) {
|
||||
box->addButton(tr::lng_send_button(), [=] {
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
state->submit = [=](Api::SendOptions options) {
|
||||
state->submitLifetime.destroy();
|
||||
const auto show = box->peerListUiShow();
|
||||
const auto peers = box->collectSelectedRows();
|
||||
const auto withPaymentApproved = crl::guard(weak, [=](
|
||||
int approved) {
|
||||
auto copy = options;
|
||||
copy.starsApproved = approved;
|
||||
if (const auto onstack = state->submit) {
|
||||
onstack(copy);
|
||||
}
|
||||
});
|
||||
|
||||
const auto alreadyApproved = options.starsApproved;
|
||||
auto paid = std::vector<not_null<PeerData*>>();
|
||||
auto waiting = base::flat_set<not_null<PeerData*>>();
|
||||
auto totalStars = 0;
|
||||
for (const auto &peer : peers) {
|
||||
const auto details = ComputePaymentDetails(peer, 1);
|
||||
if (!details) {
|
||||
waiting.emplace(peer);
|
||||
} else if (details->stars > 0) {
|
||||
totalStars += details->stars;
|
||||
paid.push_back(peer);
|
||||
}
|
||||
}
|
||||
if (!waiting.empty()) {
|
||||
session->changes().peerUpdates(
|
||||
Data::PeerUpdate::Flag::FullInfo
|
||||
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||
if (waiting.contains(update.peer)) {
|
||||
withPaymentApproved(alreadyApproved);
|
||||
}
|
||||
}, state->submitLifetime);
|
||||
|
||||
if (!session->credits().loaded()) {
|
||||
session->credits().loadedValue(
|
||||
) | rpl::filter(
|
||||
rpl::mappers::_1
|
||||
) | rpl::take(1) | rpl::start_with_next([=] {
|
||||
withPaymentApproved(alreadyApproved);
|
||||
}, state->submitLifetime);
|
||||
}
|
||||
return;
|
||||
} else if (totalStars > alreadyApproved) {
|
||||
ShowSendPaidConfirm(show, paid, SendPaymentDetails{
|
||||
.messages = 1,
|
||||
.stars = totalStars,
|
||||
}, [=] { withPaymentApproved(totalStars); });
|
||||
return;
|
||||
}
|
||||
state->submit = nullptr;
|
||||
|
||||
sendMany(ranges::views::all(
|
||||
peers
|
||||
) | ranges::views::transform([&](
|
||||
not_null<PeerData*> peer) -> Controller::Chosen {
|
||||
return peer->owner().history(peer);
|
||||
}) | ranges::to_vector);
|
||||
}) | ranges::to_vector, options);
|
||||
};
|
||||
box->addButton(tr::lng_send_button(), [=] {
|
||||
if (const auto onstack = state->submit) {
|
||||
onstack({});
|
||||
}
|
||||
});
|
||||
}
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
|
@ -2257,6 +2322,8 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
not_null<ListBox*> box;
|
||||
not_null<Controller*> controller;
|
||||
base::unique_qptr<Ui::PopupMenu> menu;
|
||||
Fn<void(Api::SendOptions options)> submit;
|
||||
rpl::lifetime submitLifetime;
|
||||
};
|
||||
|
||||
const auto applyFilter = [=](not_null<ListBox*> box, FilterId id) {
|
||||
|
@ -2401,8 +2468,62 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
session->data().message(msgIds.front())->history(),
|
||||
msgIds);
|
||||
|
||||
const auto submit = [=](Api::SendOptions options) {
|
||||
const auto weak = Ui::MakeWeak(state->box);
|
||||
state->submit = [=](Api::SendOptions options) {
|
||||
const auto peers = state->box->collectSelectedRows();
|
||||
const auto checkPaid = [=](int messagesCount) {
|
||||
const auto withPaymentApproved = crl::guard(weak, [=](
|
||||
int approved) {
|
||||
auto copy = options;
|
||||
copy.starsApproved = approved;
|
||||
if (const auto onstack = state->submit) {
|
||||
onstack(copy);
|
||||
}
|
||||
});
|
||||
|
||||
const auto alreadyApproved = options.starsApproved;
|
||||
auto paid = std::vector<not_null<PeerData*>>();
|
||||
auto waiting = base::flat_set<not_null<PeerData*>>();
|
||||
auto totalStars = 0;
|
||||
for (const auto &peer : peers) {
|
||||
const auto details = ComputePaymentDetails(
|
||||
peer,
|
||||
messagesCount);
|
||||
if (!details) {
|
||||
waiting.emplace(peer);
|
||||
} else if (details->stars > 0) {
|
||||
totalStars += details->stars;
|
||||
paid.push_back(peer);
|
||||
}
|
||||
}
|
||||
if (!waiting.empty()) {
|
||||
session->changes().peerUpdates(
|
||||
Data::PeerUpdate::Flag::FullInfo
|
||||
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||
if (waiting.contains(update.peer)) {
|
||||
withPaymentApproved(alreadyApproved);
|
||||
}
|
||||
}, state->submitLifetime);
|
||||
|
||||
if (!session->credits().loaded()) {
|
||||
session->credits().loadedValue(
|
||||
) | rpl::filter(
|
||||
rpl::mappers::_1
|
||||
) | rpl::take(1) | rpl::start_with_next([=] {
|
||||
withPaymentApproved(alreadyApproved);
|
||||
}, state->submitLifetime);
|
||||
}
|
||||
return false;
|
||||
} else if (totalStars > alreadyApproved) {
|
||||
ShowSendPaidConfirm(show, paid, SendPaymentDetails{
|
||||
.messages = messagesCount,
|
||||
.stars = totalStars,
|
||||
}, [=] { withPaymentApproved(totalStars); });
|
||||
return false;
|
||||
}
|
||||
state->submit = nullptr;
|
||||
return true;
|
||||
};
|
||||
send(
|
||||
ranges::views::all(
|
||||
peers
|
||||
|
@ -2410,11 +2531,11 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
not_null<PeerData*> peer) -> Controller::Chosen {
|
||||
return peer->owner().history(peer);
|
||||
}) | ranges::to_vector,
|
||||
[](int messagesCount) { return true; },
|
||||
checkPaid,
|
||||
comment->entity()->getTextWithAppliedMarkdown(),
|
||||
options,
|
||||
state->box->forwardOptionsData());
|
||||
if (successCallback) {
|
||||
if (!state->submit && successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
};
|
||||
|
@ -2483,7 +2604,12 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
state->menu.get(),
|
||||
show,
|
||||
SendMenu::Details{ sendMenuType() },
|
||||
SendMenu::DefaultCallback(show, crl::guard(parent, submit)));
|
||||
SendMenu::DefaultCallback(show, crl::guard(parent, [=](
|
||||
Api::SendOptions options) {
|
||||
if (const auto onstack = state->submit) {
|
||||
onstack(options);
|
||||
}
|
||||
})));
|
||||
if (showForwardOptions || !state->menu->empty()) {
|
||||
state->menu->popup(QCursor::pos());
|
||||
}
|
||||
|
@ -2505,7 +2631,11 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
const auto field = comment->entity();
|
||||
|
||||
field->submits(
|
||||
) | rpl::start_with_next([=] { submit({}); }, field->lifetime());
|
||||
) | rpl::start_with_next([=] {
|
||||
if (const auto onstack = state->submit) {
|
||||
onstack({});
|
||||
}
|
||||
}, field->lifetime());
|
||||
InitMessageFieldHandlers({
|
||||
.session = session,
|
||||
.show = show,
|
||||
|
@ -2529,9 +2659,12 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
) | rpl::start_with_next([=](bool shown) {
|
||||
state->box->clearButtons();
|
||||
if (shown) {
|
||||
const auto send = state->box->addButton(
|
||||
tr::lng_send_button(),
|
||||
[=] { submit({}); });
|
||||
auto text = tr::lng_send_button();
|
||||
const auto send = state->box->addButton(std::move(text), [=] {
|
||||
if (const auto onstack = state->submit) {
|
||||
onstack({});
|
||||
}
|
||||
});
|
||||
send->setAcceptBoth();
|
||||
send->clicks(
|
||||
) | rpl::start_with_next([=](Qt::MouseButton button) {
|
||||
|
|
|
@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
class History;
|
||||
|
||||
namespace Api {
|
||||
struct SendOptions;
|
||||
} // namespace Api
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
class BoxContent;
|
||||
|
@ -146,7 +150,9 @@ object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
|||
rpl::producer<QString> titleOverride = nullptr,
|
||||
FnMut<void()> &&successCallback = nullptr,
|
||||
InlineBots::PeerTypes typesRestriction = 0,
|
||||
Fn<void(std::vector<not_null<Data::Thread*>>)> sendMany = nullptr);
|
||||
Fn<void(
|
||||
std::vector<not_null<Data::Thread*>>,
|
||||
Api::SendOptions)> sendMany = nullptr);
|
||||
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||
|
|
Loading…
Add table
Reference in a new issue