Allow removing a bot from attach menu.

This commit is contained in:
John Preston 2022-03-29 20:12:47 +04:00
parent 72ae2f0269
commit b38ac32898
5 changed files with 128 additions and 26 deletions

View file

@ -1722,6 +1722,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_sure_add_text_group" = "Are you sure you want to add this bot as an admin in the group {group}?";
"lng_bot_sure_add_text_channel" = "Are you sure you want to add this bot as an admin in the channel {group}?";
"lng_bot_sure_add" = "Add as admin";
"lng_bot_no_webview" = "Unfortunately, you can't open such menu with current system configuration.";
"lng_bot_remove_from_menu" = "Remove from menu";
"lng_bot_remove_from_menu_done" = "Bot removed from the menu.";
"lng_bot_add_to_menu" = "{bot} asks your permission to be added as an option to your attachments menu so you can access it from any chat.";
"lng_bot_add_to_menu_done" = "Bot added to the menu.";
"lng_bot_menu_not_supported" = "This bot isn't supported in the attach menu.";
"lng_bot_menu_already_added" = "This bot is already added in your attach menu.";
"lng_typing" = "typing";
"lng_user_typing" = "{user} is typing";

View file

@ -489,9 +489,18 @@ HistoryWidget::HistoryWidget(
_attachBotsMenu = nullptr;
return;
} else if (!_attachBotsMenu) {
const auto forceShown = [=](bool shown) {
if (shown) {
_attachBotsMenu->setAutoHiding(false);
} else {
_attachBotsMenu->hideAnimated();
_attachBotsMenu->setAutoHiding(true);
}
};
_attachBotsMenu = InlineBots::MakeAttachBotsMenu(
this,
controller);
controller,
forceShown);
_attachBotsMenu->setOrigin(
Ui::PanelAnimation::Origin::BottomLeft);
if (_history && _history->peer->isUser()) {

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_file_origin.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "main/main_domain.h"
@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toasts/common_toasts.h"
#include "ui/chat/attach/attach_bot_webview.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/menu/menu_item_base.h"
#include "ui/effects/ripple_animation.h"
#include "window/themes/window_theme.h"
@ -32,6 +34,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "styles/style_menu_icons.h"
#include <QSvgRenderer>
namespace InlineBots {
namespace {
@ -81,23 +85,32 @@ public:
bool isEnabled() const override;
not_null<QAction*> action() const override;
[[nodiscard]] rpl::producer<bool> forceShown() const;
void handleKeyPress(not_null<QKeyEvent*> e) override;
protected:
private:
void contextMenuEvent(QContextMenuEvent *e) override;
QPoint prepareRippleStartPosition() const override;
QImage prepareRippleMask() const override;
int contentHeight() const override;
private:
void prepare();
void validateIcon();
void paint(Painter &p);
const not_null<QAction*> _dummyAction;
const style::Menu &_st;
const AttachWebViewBot _bot;
base::unique_qptr<Ui::PopupMenu> _menu;
rpl::event_stream<bool> _forceShown;
Ui::Text::String _text;
QImage _mask;
QImage _icon;
int _textWidth = 0;
const int _height;
@ -115,7 +128,7 @@ BotAction::BotAction(
, _height(_st.itemPadding.top()
+ _st.itemStyle.font->height
+ _st.itemPadding.bottom()) {
setAcceptBoth(true);
setAcceptBoth(false);
initResizeHook(parent->sizeValue());
setClickedCallback(std::move(callback));
@ -125,11 +138,48 @@ BotAction::BotAction(
paint(p);
}, lifetime());
style::PaletteChanged(
) | rpl::start_with_next([=] {
_icon = QImage();
update();
}, lifetime());
enableMouseSelecting();
prepare();
}
void BotAction::validateIcon() {
if (_mask.isNull()) {
if (!_bot.media->loaded()) {
return;
}
auto icon = QSvgRenderer(_bot.media->bytes());
if (!icon.isValid()) {
_mask = QImage(
QSize(1, 1) * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
_mask.fill(Qt::transparent);
} else {
const auto size = style::ConvertScale(icon.defaultSize());
_mask = QImage(
size * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
_mask.fill(Qt::transparent);
{
auto p = QPainter(&_mask);
icon.render(&p, QRect(QPoint(), size));
}
_mask = Images::Colored(std::move(_mask), QColor(255, 255, 255));
}
}
if (_icon.isNull()) {
_icon = style::colorizeImage(_mask, st::menuIconColor);
}
}
void BotAction::paint(Painter &p) {
validateIcon();
const auto selected = isSelected();
if (selected && _st.itemBgOver->c.alpha() < 255) {
p.fillRect(0, 0, width(), _height, _st.itemBg);
@ -139,14 +189,9 @@ void BotAction::paint(Painter &p) {
paintRipple(p, 0, 0);
}
const auto normalHeight = _st.itemPadding.top()
+ _st.itemStyle.font->height
+ _st.itemPadding.bottom();
const auto deltaHeight = _height - normalHeight;
st::menuIconDelete.paint(
p,
_st.itemIconPosition + QPoint(0, deltaHeight / 2),
width());
if (!_icon.isNull()) {
p.drawImage(_st.itemIconPosition, _icon);
}
p.setPen(selected ? _st.itemFgOver : _st.itemFg);
_text.drawLeftElided(
@ -180,6 +225,24 @@ not_null<QAction*> BotAction::action() const {
return _dummyAction;
}
void BotAction::contextMenuEvent(QContextMenuEvent *e) {
_menu = nullptr;
_menu = base::make_unique_q<Ui::PopupMenu>(
this,
st::popupMenuWithIcons);
_menu->addAction(tr::lng_bot_remove_from_menu(tr::now), [=] {
_bot.user->session().attachWebView().removeFromMenu(_bot.user);
}, &st::menuIconDelete);
QObject::connect(_menu, &QObject::destroyed, [=] {
_forceShown.fire(false);
});
_forceShown.fire(true);
_menu->popup(e->globalPos());
e->accept();
}
QPoint BotAction::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos());
}
@ -192,6 +255,10 @@ int BotAction::contentHeight() const {
return _height;
}
rpl::producer<bool> BotAction::forceShown() const {
return _forceShown.events();
}
void BotAction::handleKeyPress(not_null<QKeyEvent*> e) {
if (!isSelected()) {
return;
@ -348,7 +415,8 @@ void AttachWebView::requestAddToMenu(not_null<UserData*> bot) {
} else {
requestBots();
Ui::ShowMultilineToast({
.text = { u"Bot is already added."_q },
.text = {
tr::lng_bot_menu_already_added(tr::now) },
});
}
}
@ -358,13 +426,17 @@ void AttachWebView::requestAddToMenu(not_null<UserData*> bot) {
_addToMenuId = 0;
_addToMenuBot = nullptr;
Ui::ShowMultilineToast({
.text = { u"Bot cannot be added to the menu."_q },
});
.text = { tr::lng_bot_menu_not_supported(tr::now) },
});
}).send();
}
void AttachWebView::removeFromMenu(not_null<UserData*> bot) {
toggleInMenu(bot, false, nullptr);
toggleInMenu(bot, false, [=] {
Ui::ShowMultilineToast({
.text = { tr::lng_bot_remove_from_menu_done(tr::now) },
});
});
}
void AttachWebView::resolve() {
@ -378,8 +450,7 @@ void AttachWebView::requestByUsername() {
_bot = bot->asUser();
if (!_bot || !_bot->isBot() || !_bot->botInfo->supportsAttachMenu) {
Ui::ShowMultilineToast({
// #TODO webview lang
.text = { u"This bot isn't supported in the attach menu."_q }
.text = { tr::lng_bot_menu_not_supported(tr::now) }
});
return;
}
@ -529,15 +600,18 @@ void AttachWebView::confirmAddToMenu(
if (callback) {
callback();
}
close();
Ui::ShowMultilineToast({
.text = { tr::lng_bot_add_to_menu_done(tr::now) },
});
});
close();
};
const auto active = Core::App().activeWindow();
if (!active) {
return;
}
_confirmAddBox = active->show(Ui::MakeConfirmBox({
u"Do you want to? "_q + bot.name,
tr::lng_bot_add_to_menu(tr::now, lt_bot, bot.name),
done,
}));
}
@ -562,7 +636,8 @@ void AttachWebView::toggleInMenu(
std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller) {
not_null<Window::SessionController*> controller,
Fn<void(bool)> forceShown) {
auto result = std::make_unique<Ui::DropdownMenu>(
parent,
st::dropdownMenuWithIcons);
@ -571,12 +646,22 @@ std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
const auto refresh = [=] {
raw->clearActions();
for (const auto &bot : bots->attachBots()) {
raw->addAction(base::make_unique_q<BotAction>(raw, bot, raw->menu()->st(), [=] {
const auto callback = [=] {
const auto active = controller->activeChatCurrent();
if (const auto history = active.history()) {
bots->request(history->peer, bot.user);
}
}));
};
auto action = base::make_unique_q<BotAction>(
raw,
raw->menu()->st(),
bot,
callback);
action->forceShown(
) | rpl::start_with_next([=](bool shown) {
forceShown(shown);
}, action->lifetime());
raw->addAction(std::move(action));
}
};
refresh();

View file

@ -85,7 +85,7 @@ private:
void toggleInMenu(
not_null<UserData*> bot,
bool enabled,
Fn<void()> callback);
Fn<void()> callback = nullptr);
void show(
uint64 queryId,
@ -122,6 +122,7 @@ private:
[[nodiscard]] std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller);
not_null<Window::SessionController*> controller,
Fn<void(bool)> forceShown);
} // namespace InlineBots

View file

@ -482,7 +482,7 @@ std::unique_ptr<Panel> Show(Args &&args) {
const auto available = Webview::Availability();
if (available.error != Webview::Available::Error::None) {
result->showWebviewError(
tr::lng_payments_webview_no_card(tr::now),
tr::lng_bot_no_webview(tr::now),
available);
} else {
result->showCriticalError({