Make nice share message from miniapp.

This commit is contained in:
John Preston 2024-11-14 22:26:41 +04:00
parent 7bf78b3317
commit 2fed657940
7 changed files with 188 additions and 48 deletions

View file

@ -1065,6 +1065,11 @@ std::unique_ptr<PeerListRow> ChooseTopicBoxController::createSearchRow(
return nullptr; return nullptr;
} }
std::unique_ptr<PeerListRow> ChooseTopicBoxController::MakeRow(
not_null<Data::ForumTopic*> topic) {
return std::make_unique<Row>(topic);
}
auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic) auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
-> std::unique_ptr<Row> { -> std::unique_ptr<Row> {
const auto skip = _filter && !_filter(topic); const auto skip = _filter && !_filter(topic);

View file

@ -335,6 +335,9 @@ public:
void loadMoreRows() override; void loadMoreRows() override;
std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id) override; std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id) override;
[[nodiscard]] static std::unique_ptr<PeerListRow> MakeRow(
not_null<Data::ForumTopic*> topic);
private: private:
class Row final : public PeerListRow { class Row final : public PeerListRow {
public: public:

View file

@ -12,12 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h" #include "base/unixtime.h"
#include "boxes/gift_premium_box.h" #include "boxes/gift_premium_box.h"
#include "boxes/peer_list_box.h" #include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/share_box.h" #include "boxes/share_box.h"
#include "core/application.h" #include "core/application.h"
#include "core/ui_integration.h" // Core::MarkedTextContext. #include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/components/credits.h" #include "data/components/credits.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_forum_topic.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -51,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_credits.h" #include "styles/style_credits.h"
#include "styles/style_dialogs.h"
#include "styles/style_giveaway.h" #include "styles/style_giveaway.h"
#include "styles/style_info.h" #include "styles/style_info.h"
#include "styles/style_layers.h" // st::boxDividerLabel. #include "styles/style_layers.h" // st::boxDividerLabel.
@ -264,8 +267,9 @@ private:
class SingleRowController final : public PeerListController { class SingleRowController final : public PeerListController {
public: public:
SingleRowController( SingleRowController(
not_null<PeerData*> peer, not_null<Data::Thread*> thread,
rpl::producer<QString> status); rpl::producer<QString> status,
Fn<void()> clicked);
void prepare() override; void prepare() override;
void loadMoreRows() override; void loadMoreRows() override;
@ -273,8 +277,10 @@ public:
Main::Session &session() const override; Main::Session &session() const override;
private: private:
const not_null<PeerData*> _peer; const not_null<Main::Session*> _session;
const base::weak_ptr<Data::Thread> _thread;
rpl::producer<QString> _status; rpl::producer<QString> _status;
Fn<void()> _clicked;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
}; };
@ -1144,36 +1150,59 @@ int Controller::descriptionTopSkipMin() const {
} }
SingleRowController::SingleRowController( SingleRowController::SingleRowController(
not_null<PeerData*> peer, not_null<Data::Thread*> thread,
rpl::producer<QString> status) rpl::producer<QString> status,
: _peer(peer) Fn<void()> clicked)
, _status(std::move(status)) { : _session(&thread->session())
, _thread(thread)
, _status(std::move(status))
, _clicked(std::move(clicked)) {
} }
void SingleRowController::prepare() { void SingleRowController::prepare() {
auto row = std::make_unique<PeerListRow>(_peer); const auto strong = _thread.get();
if (!strong) {
return;
}
const auto topic = strong->asTopic();
auto row = topic
? ChooseTopicBoxController::MakeRow(topic)
: std::make_unique<PeerListRow>(strong->peer());
const auto raw = row.get(); const auto raw = row.get();
std::move( if (_status) {
_status std::move(
) | rpl::start_with_next([=](const QString &status) { _status
raw->setCustomStatus(status); ) | rpl::start_with_next([=](const QString &status) {
delegate()->peerListUpdateRow(raw); raw->setCustomStatus(status);
}, _lifetime); delegate()->peerListUpdateRow(raw);
}, _lifetime);
}
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
if (topic) {
topic->destroyed() | rpl::start_with_next([=] {
while (delegate()->peerListFullRowsCount()) {
delegate()->peerListRemoveRow(delegate()->peerListRowAt(0));
}
delegate()->peerListRefreshRows();
}, _lifetime);
}
} }
void SingleRowController::loadMoreRows() { void SingleRowController::loadMoreRows() {
} }
void SingleRowController::rowClicked(not_null<PeerListRow*> row) { void SingleRowController::rowClicked(not_null<PeerListRow*> row) {
ShowPeerInfoSync(row->peer()); if (const auto onstack = _clicked) {
onstack();
} else {
ShowPeerInfoSync(row->peer());
}
} }
Main::Session &SingleRowController::session() const { Main::Session &SingleRowController::session() const {
return _peer->session(); return *_session;
} }
} // namespace } // namespace
@ -1186,14 +1215,29 @@ bool IsExpiredLink(const Api::InviteLink &data, TimeId now) {
void AddSinglePeerRow( void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer, not_null<PeerData*> peer,
rpl::producer<QString> status) { rpl::producer<QString> status,
Fn<void()> clicked) {
AddSinglePeerRow(
container,
peer->owner().history(peer),
std::move(status),
std::move(clicked));
}
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked) {
const auto delegate = container->lifetime().make_state< const auto delegate = container->lifetime().make_state<
PeerListContentDelegateSimple PeerListContentDelegateSimple
>(); >();
const auto controller = container->lifetime().make_state< const auto controller = container->lifetime().make_state<
SingleRowController SingleRowController
>(peer, std::move(status)); >(thread, std::move(status), std::move(clicked));
controller->setStyleOverrides(&st::peerListSingleRow); controller->setStyleOverrides(thread->asTopic()
? &st::chooseTopicList
: &st::peerListSingleRow);
const auto content = container->add(object_ptr<PeerListContent>( const auto content = container->add(object_ptr<PeerListContent>(
container, container,
controller)); controller));

View file

@ -16,6 +16,10 @@ namespace Api {
struct InviteLink; struct InviteLink;
} // namespace Api } // namespace Api
namespace Data {
class Thread;
} // namespace Data
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -31,7 +35,14 @@ class BoxContent;
void AddSinglePeerRow( void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer, not_null<PeerData*> peer,
rpl::producer<QString> status); rpl::producer<QString> status,
Fn<void()> clicked = nullptr);
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked = nullptr);
void AddPermanentLinkBlock( void AddPermanentLinkBlock(
std::shared_ptr<Ui::Show> show, std::shared_ptr<Ui::Show> show,

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_common.h" #include "api/api_common.h"
#include "api/api_sending.h" #include "api/api_sending.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "base/call_delayed.h"
#include "base/qthelp_url.h" #include "base/qthelp_url.h"
#include "base/random.h" #include "base/random.h"
#include "base/timer_rpl.h" #include "base/timer_rpl.h"
@ -60,6 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/vertical_list.h" #include "ui/vertical_list.h"
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/dropdown_menu.h" #include "ui/widgets/dropdown_menu.h"
@ -1786,29 +1788,23 @@ void WebViewInstance::botSendPreparedMessage(
}); });
struct State { struct State {
QPointer<Ui::BoxContent> preview; QPointer<Ui::BoxContent> preview;
rpl::event_stream<not_null<Data::Thread*>> recipient;
bool sent = false; bool sent = false;
}; };
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
auto box = Box(PreparedPreviewBox, item, [=] { auto recipient = state->recipient.events();
auto box = Box(PreparedPreviewBox, item, std::move(recipient), [=] {
if (state->sent) {
return;
}
const auto chosen = [=](not_null<Data::Thread*> thread) { const auto chosen = [=](not_null<Data::Thread*> thread) {
auto action = Api::SendAction(thread); if (!Data::CanSend(thread, ChatRestriction::SendInline)) {
const auto done = [=](bool success) { panel->showToast({
if (success) { tr::lng_restricted_send_inline_all(tr::now),
callback(QString()); });
} else { return false;
callback(u"MESSAGE_SEND_FAILED"_q);
}
};
bot->session().api().sendInlineResult(
bot,
parsed.get(),
action,
std::nullopt,
done);
state->sent = true;
if (const auto strong = state->preview.data()) {
strong->closeBox();
} }
state->recipient.fire_copy(thread);
return true; return true;
}; };
auto box = Window::PrepareChooseRecipientBox( auto box = Window::PrepareChooseRecipientBox(
@ -1818,6 +1814,36 @@ void WebViewInstance::botSendPreparedMessage(
nullptr, nullptr,
types); types);
panel->showBox(std::move(box)); panel->showBox(std::move(box));
}, [=](not_null<Data::Thread*> thread) {
if (state->sent) {
return;
}
state->sent = true;
const auto weak = state->preview;
const auto done = [=](bool success) {
if (success) {
if (const auto strong = weak.data()) {
strong->showToast({ tr::lng_share_done(tr::now) });
}
base::call_delayed(Ui::Toast::kDefaultDuration, [weak] {
if (const auto strong = weak.data()) {
strong->closeBox();
}
});
callback(QString());
} else {
if (const auto strong = weak.data()) {
strong->closeBox();
}
callback(u"MESSAGE_SEND_FAILED"_q);
}
};
bot->session().api().sendInlineResult(
bot,
parsed.get(),
Api::SendAction(thread),
std::nullopt,
done);
}); });
box->boxClosing() | rpl::start_with_next([=] { box->boxClosing() | rpl::start_with_next([=] {
if (!state->sent) { if (!state->sent) {

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "inline_bots/inline_bot_confirm_prepared.h" #include "inline_bots/inline_bot_confirm_prepared.h"
#include "boxes/peers/edit_peer_invite_link.h"
#include "data/data_forum_topic.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "history/admin_log/history_admin_log_item.h" #include "history/admin_log/history_admin_log_item.h"
@ -19,11 +21,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/chat_theme.h" #include "ui/chat/chat_theme.h"
#include "ui/effects/path_shift_gradient.h" #include "ui/effects/path_shift_gradient.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/vertical_list.h" #include "ui/vertical_list.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "window/section_widget.h" #include "window/section_widget.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_layers.h"
namespace InlineBots { namespace InlineBots {
namespace { namespace {
@ -168,22 +172,63 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
_item->draw(p, context); _item->draw(p, context);
} }
} // namesace } // namespace
void PreparedPreviewBox( void PreparedPreviewBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
Fn<void()> share) { rpl::producer<not_null<Data::Thread*>> recipient,
Fn<void()> choose,
Fn<void(not_null<Data::Thread*>)> send) {
box->setTitle(tr::lng_bot_share_prepared_title()); box->setTitle(tr::lng_bot_share_prepared_title());
const auto container = box->verticalLayout(); const auto container = box->verticalLayout();
container->add(object_ptr<PreviewWrap>(container, item)); container->add(object_ptr<PreviewWrap>(container, item));
const auto bot = item->viaBot(); const auto bot = item->viaBot();
const auto name = bot ? bot->name() : u"Bot"_q; const auto name = bot ? bot->name() : u"Bot"_q;
Ui::AddDividerText( const auto info = container->add(
container, object_ptr<Ui::SlideWrap<Ui::DividerLabel>>(
tr::lng_bot_share_prepared_about(lt_bot, rpl::single(name))), container,
box->addButton(tr::lng_bot_share_prepared_button(), share); object_ptr<Ui::DividerLabel>(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); container,
object_ptr<Ui::FlatLabel>(
container,
tr::lng_bot_share_prepared_about(lt_bot, rpl::single(name)),
st::boxDividerLabel),
st::defaultBoxDividerLabelPadding,
RectPart::Top | RectPart::Bottom)));
const auto row = container->add(object_ptr<Ui::VerticalLayout>(
container));
const auto reset = [=] {
info->show(anim::type::instant);
while (row->count()) {
delete row->widgetAt(0);
}
box->addButton(tr::lng_bot_share_prepared_button(), choose);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
};
reset();
const auto lifetime = box->lifetime().make_state<rpl::lifetime>();
std::move(
recipient
) | rpl::start_with_next([=](not_null<Data::Thread*> thread) {
info->hide(anim::type::instant);
while (row->count()) {
delete row->widgetAt(0);
}
AddSkip(row);
AddSinglePeerRow(row, thread, nullptr, choose);
if (const auto topic = thread->asTopic()) {
*lifetime = topic->destroyed() | rpl::start_with_next(reset);
} else {
*lifetime = rpl::lifetime();
}
row->resizeToWidth(container->width());
box->clearButtons();
box->addButton(tr::lng_send_button(), [=] { send(thread); });
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}, info->lifetime());
item->history()->owner().itemRemoved( item->history()->owner().itemRemoved(
) | rpl::start_with_next([=](not_null<const HistoryItem*> removed) { ) | rpl::start_with_next([=](not_null<const HistoryItem*> removed) {

View file

@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
namespace Data {
class Thread;
} // namespace Data
namespace Ui { namespace Ui {
class GenericBox; class GenericBox;
} // namespace Ui } // namespace Ui
@ -16,6 +20,8 @@ namespace InlineBots {
void PreparedPreviewBox( void PreparedPreviewBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
Fn<void()> share); rpl::producer<not_null<Data::Thread*>> recipient,
Fn<void()> choose,
Fn<void(not_null<Data::Thread*>)> sent);
} // namespace InlineBots } // namespace InlineBots