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;
}
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)
-> std::unique_ptr<Row> {
const auto skip = _filter && !_filter(topic);

View file

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

View file

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

View file

@ -16,6 +16,10 @@ namespace Api {
struct InviteLink;
} // namespace Api
namespace Data {
class Thread;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@ -31,7 +35,14 @@ class BoxContent;
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
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(
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_sending.h"
#include "apiwrap.h"
#include "base/call_delayed.h"
#include "base/qthelp_url.h"
#include "base/random.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/painter.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/vertical_list.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/dropdown_menu.h"
@ -1786,29 +1788,23 @@ void WebViewInstance::botSendPreparedMessage(
});
struct State {
QPointer<Ui::BoxContent> preview;
rpl::event_stream<not_null<Data::Thread*>> recipient;
bool sent = false;
};
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) {
auto action = Api::SendAction(thread);
const auto done = [=](bool success) {
if (success) {
callback(QString());
} else {
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();
if (!Data::CanSend(thread, ChatRestriction::SendInline)) {
panel->showToast({
tr::lng_restricted_send_inline_all(tr::now),
});
return false;
}
state->recipient.fire_copy(thread);
return true;
};
auto box = Window::PrepareChooseRecipientBox(
@ -1818,6 +1814,36 @@ void WebViewInstance::botSendPreparedMessage(
nullptr,
types);
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([=] {
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 "boxes/peers/edit_peer_invite_link.h"
#include "data/data_forum_topic.h"
#include "data/data_session.h"
#include "data/data_user.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/effects/path_shift_gradient.h"
#include "ui/layers/generic_box.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "window/themes/window_theme.h"
#include "window/section_widget.h"
#include "styles/style_chat.h"
#include "styles/style_layers.h"
namespace InlineBots {
namespace {
@ -168,22 +172,63 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
_item->draw(p, context);
}
} // namesace
} // namespace
void PreparedPreviewBox(
not_null<Ui::GenericBox*> box,
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());
const auto container = box->verticalLayout();
container->add(object_ptr<PreviewWrap>(container, item));
const auto bot = item->viaBot();
const auto name = bot ? bot->name() : u"Bot"_q;
Ui::AddDividerText(
container,
tr::lng_bot_share_prepared_about(lt_bot, rpl::single(name))),
box->addButton(tr::lng_bot_share_prepared_button(), share);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
const auto info = container->add(
object_ptr<Ui::SlideWrap<Ui::DividerLabel>>(
container,
object_ptr<Ui::DividerLabel>(
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(
) | 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
namespace Data {
class Thread;
} // namespace Data
namespace Ui {
class GenericBox;
} // namespace Ui
@ -16,6 +20,8 @@ namespace InlineBots {
void PreparedPreviewBox(
not_null<Ui::GenericBox*> box,
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