mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Start main menu bots.
This commit is contained in:
parent
73f3110403
commit
fbd8abc1c6
12 changed files with 369 additions and 154 deletions
|
@ -2260,6 +2260,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_bot_remove_from_menu" = "Remove From Menu";
|
"lng_bot_remove_from_menu" = "Remove From Menu";
|
||||||
"lng_bot_remove_from_menu_sure" = "Remove {bot} from the attachment menu?";
|
"lng_bot_remove_from_menu_sure" = "Remove {bot} from the attachment menu?";
|
||||||
"lng_bot_remove_from_menu_done" = "Bot removed from the menu.";
|
"lng_bot_remove_from_menu_done" = "Bot removed from the menu.";
|
||||||
|
"lng_bot_remove_from_side_menu" = "Remove From Menu";
|
||||||
|
"lng_bot_remove_from_side_menu_sure" = "Remove {bot} from the main menu?";
|
||||||
|
"lng_bot_remove_from_side_menu_done" = "Bot removed from the main menu.";
|
||||||
"lng_bot_settings" = "Settings";
|
"lng_bot_settings" = "Settings";
|
||||||
"lng_bot_open" = "Open Bot";
|
"lng_bot_open" = "Open Bot";
|
||||||
"lng_bot_reload_page" = "Reload Page";
|
"lng_bot_reload_page" = "Reload Page";
|
||||||
|
@ -2271,6 +2274,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_bot_close_warning_title" = "Warning";
|
"lng_bot_close_warning_title" = "Warning";
|
||||||
"lng_bot_close_warning" = "Changes that you made may not be saved.";
|
"lng_bot_close_warning" = "Changes that you made may not be saved.";
|
||||||
"lng_bot_close_warning_sure" = "Close anyway";
|
"lng_bot_close_warning_sure" = "Close anyway";
|
||||||
|
"lng_bot_add_to_side_menu" = "{bot} asks your permission to be added as an option to your main menu so you can access it any time.";
|
||||||
|
"lng_bot_add_to_side_menu_done" = "Bot added to the main menu.";
|
||||||
|
|
||||||
"lng_typing" = "typing";
|
"lng_typing" = "typing";
|
||||||
"lng_user_typing" = "{user} is typing";
|
"lng_user_typing" = "{user} is typing";
|
||||||
|
@ -3834,6 +3839,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
"lng_telegram_features_url" = "https://t.me/TelegramTips";
|
"lng_telegram_features_url" = "https://t.me/TelegramTips";
|
||||||
|
|
||||||
|
"lng_mini_apps_disclaimer_title" = "Warning";
|
||||||
|
"lng_mini_apps_disclaimer_text" = "You are about to use a mini app operated by an independent party **not affiliated with Telegram**. You must agree to the Terms of Use of mini apps to continue.";
|
||||||
|
"lng_mini_apps_disclaimer_button" = "I agree to the {link}";
|
||||||
|
"lng_mini_apps_disclaimer_link" = "Terms of Use";
|
||||||
|
"lng_mini_apps_tos_url" = "https://telegram.org/tos/mini-apps";
|
||||||
|
|
||||||
"lng_ringtones_box_title" = "Notification Sound";
|
"lng_ringtones_box_title" = "Notification Sound";
|
||||||
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
|
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
|
||||||
"lng_ringtones_box_upload_choose" = "Choose ringtone";
|
"lng_ringtones_box_upload_choose" = "Choose ringtone";
|
||||||
|
|
|
@ -392,7 +392,6 @@ bool ResolveUsernameOrPhone(
|
||||||
const auto storyParam = params.value(u"story"_q);
|
const auto storyParam = params.value(u"story"_q);
|
||||||
const auto storyId = storyParam.toInt();
|
const auto storyId = storyParam.toInt();
|
||||||
const auto appname = webChannelPreviewLink ? QString() : appnameParam;
|
const auto appname = webChannelPreviewLink ? QString() : appnameParam;
|
||||||
const auto appstart = params.value(u"startapp"_q);
|
|
||||||
const auto commentParam = params.value(u"comment"_q);
|
const auto commentParam = params.value(u"comment"_q);
|
||||||
const auto commentId = commentParam.toInt();
|
const auto commentId = commentParam.toInt();
|
||||||
const auto topicParam = params.value(u"topic"_q);
|
const auto topicParam = params.value(u"topic"_q);
|
||||||
|
@ -404,11 +403,11 @@ bool ResolveUsernameOrPhone(
|
||||||
startToken = gameParam;
|
startToken = gameParam;
|
||||||
resolveType = ResolveType::ShareGame;
|
resolveType = ResolveType::ShareGame;
|
||||||
}
|
}
|
||||||
if (startToken.isEmpty() && params.contains(u"startapp"_q)) {
|
|
||||||
startToken = params.value(u"startapp"_q);
|
|
||||||
}
|
|
||||||
if (!appname.isEmpty()) {
|
if (!appname.isEmpty()) {
|
||||||
resolveType = ResolveType::BotApp;
|
resolveType = ResolveType::BotApp;
|
||||||
|
if (startToken.isEmpty() && params.contains(u"startapp"_q)) {
|
||||||
|
startToken = params.value(u"startapp"_q);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const auto myContext = context.value<ClickHandlerContext>();
|
const auto myContext = context.value<ClickHandlerContext>();
|
||||||
using Navigation = Window::SessionNavigation;
|
using Navigation = Window::SessionNavigation;
|
||||||
|
@ -437,6 +436,8 @@ bool ResolveUsernameOrPhone(
|
||||||
.attachBotToggleCommand = (params.contains(u"startattach"_q)
|
.attachBotToggleCommand = (params.contains(u"startattach"_q)
|
||||||
? params.value(u"startattach"_q)
|
? params.value(u"startattach"_q)
|
||||||
: std::optional<QString>()),
|
: std::optional<QString>()),
|
||||||
|
.attachBotMenuOpen = (appname.isEmpty()
|
||||||
|
&& params.contains(u"startapp"_q)),
|
||||||
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
|
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
|
||||||
params.value(u"choose"_q)),
|
params.value(u"choose"_q)),
|
||||||
.voicechatHash = (params.contains(u"livestream"_q)
|
.voicechatHash = (params.contains(u"livestream"_q)
|
||||||
|
|
|
@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
#include <QSvgRenderer>
|
#include <QSvgRenderer>
|
||||||
|
@ -56,11 +57,7 @@ namespace InlineBots {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kProlongTimeout = 60 * crl::time(1000);
|
constexpr auto kProlongTimeout = 60 * crl::time(1000);
|
||||||
|
constexpr auto kRefreshBotsTimeout = 60 * 60 * crl::time(1000);
|
||||||
struct ParsedBot {
|
|
||||||
UserData *bot = nullptr;
|
|
||||||
bool inactive = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] DocumentData *ResolveIcon(
|
[[nodiscard]] DocumentData *ResolveIcon(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
|
@ -113,8 +110,13 @@ struct ParsedBot {
|
||||||
.user = user,
|
.user = user,
|
||||||
.icon = ResolveIcon(session, data),
|
.icon = ResolveIcon(session, data),
|
||||||
.name = qs(data.vshort_name()),
|
.name = qs(data.vshort_name()),
|
||||||
.types = ResolvePeerTypes(data.vpeer_types().v),
|
.types = (data.vpeer_types()
|
||||||
|
? ResolvePeerTypes(data.vpeer_types()->v)
|
||||||
|
: PeerTypes()),
|
||||||
.inactive = data.is_inactive(),
|
.inactive = data.is_inactive(),
|
||||||
|
.inMainMenu = data.is_show_in_side_menu(),
|
||||||
|
.inAttachMenu = data.is_show_in_attach_menu(),
|
||||||
|
.disclaimerRequired = data.is_side_menu_disclaimer_needed(),
|
||||||
.hasSettings = data.is_has_settings(),
|
.hasSettings = data.is_has_settings(),
|
||||||
.requestWriteAccess = data.is_request_write_access(),
|
.requestWriteAccess = data.is_request_write_access(),
|
||||||
} : std::optional<AttachWebViewBot>();
|
} : std::optional<AttachWebViewBot>();
|
||||||
|
@ -216,19 +218,18 @@ private:
|
||||||
int contentHeight() const override;
|
int contentHeight() const override;
|
||||||
|
|
||||||
void prepare();
|
void prepare();
|
||||||
void validateIcon();
|
|
||||||
void paint(Painter &p);
|
void paint(Painter &p);
|
||||||
|
|
||||||
const not_null<QAction*> _dummyAction;
|
const not_null<QAction*> _dummyAction;
|
||||||
const style::Menu &_st;
|
const style::Menu &_st;
|
||||||
const AttachWebViewBot _bot;
|
const AttachWebViewBot _bot;
|
||||||
|
|
||||||
|
MenuBotIcon _icon;
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
rpl::event_stream<bool> _forceShown;
|
rpl::event_stream<bool> _forceShown;
|
||||||
|
|
||||||
Ui::Text::String _text;
|
Ui::Text::String _text;
|
||||||
QImage _mask;
|
|
||||||
QImage _icon;
|
|
||||||
int _textWidth = 0;
|
int _textWidth = 0;
|
||||||
const int _height;
|
const int _height;
|
||||||
|
|
||||||
|
@ -243,6 +244,7 @@ BotAction::BotAction(
|
||||||
, _dummyAction(new QAction(parent))
|
, _dummyAction(new QAction(parent))
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _bot(bot)
|
, _bot(bot)
|
||||||
|
, _icon(this, _bot.media)
|
||||||
, _height(_st.itemPadding.top()
|
, _height(_st.itemPadding.top()
|
||||||
+ _st.itemStyle.font->height
|
+ _st.itemStyle.font->height
|
||||||
+ _st.itemPadding.bottom()) {
|
+ _st.itemPadding.bottom()) {
|
||||||
|
@ -250,55 +252,19 @@ BotAction::BotAction(
|
||||||
initResizeHook(parent->sizeValue());
|
initResizeHook(parent->sizeValue());
|
||||||
setClickedCallback(std::move(callback));
|
setClickedCallback(std::move(callback));
|
||||||
|
|
||||||
|
_icon.move(_st.itemIconPosition);
|
||||||
|
|
||||||
paintRequest(
|
paintRequest(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
paint(p);
|
paint(p);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
style::PaletteChanged(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
_icon = QImage();
|
|
||||||
update();
|
|
||||||
}, lifetime());
|
|
||||||
|
|
||||||
enableMouseSelecting();
|
enableMouseSelecting();
|
||||||
prepare();
|
prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BotAction::validateIcon() {
|
|
||||||
if (_mask.isNull()) {
|
|
||||||
if (!_bot.media || !_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.setDevicePixelRatio(style::DevicePixelRatio());
|
|
||||||
_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) {
|
void BotAction::paint(Painter &p) {
|
||||||
validateIcon();
|
|
||||||
|
|
||||||
const auto selected = isSelected();
|
const auto selected = isSelected();
|
||||||
if (selected && _st.itemBgOver->c.alpha() < 255) {
|
if (selected && _st.itemBgOver->c.alpha() < 255) {
|
||||||
p.fillRect(0, 0, width(), _height, _st.itemBg);
|
p.fillRect(0, 0, width(), _height, _st.itemBg);
|
||||||
|
@ -308,10 +274,6 @@ void BotAction::paint(Painter &p) {
|
||||||
paintRipple(p, 0, 0);
|
paintRipple(p, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_icon.isNull()) {
|
|
||||||
p.drawImage(_st.itemIconPosition, _icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.setPen(selected ? _st.itemFgOver : _st.itemFg);
|
p.setPen(selected ? _st.itemFgOver : _st.itemFg);
|
||||||
_text.drawLeftElided(
|
_text.drawLeftElided(
|
||||||
p,
|
p,
|
||||||
|
@ -390,6 +352,53 @@ void BotAction::handleKeyPress(not_null<QKeyEvent*> e) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
MenuBotIcon::MenuBotIcon(
|
||||||
|
QWidget *parent,
|
||||||
|
std::shared_ptr<Data::DocumentMedia> media)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _media(std::move(media)) {
|
||||||
|
style::PaletteChanged(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_image = QImage();
|
||||||
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
resize(st::menuIconAdmin.size());
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuBotIcon::paintEvent(QPaintEvent *e) {
|
||||||
|
validate();
|
||||||
|
if (!_image.isNull()) {
|
||||||
|
QPainter(this).drawImage(0, 0, _image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuBotIcon::validate() {
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto wanted = size() * ratio;
|
||||||
|
if (_mask.size() != wanted) {
|
||||||
|
if (!_media || !_media->loaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto icon = QSvgRenderer(_media->bytes());
|
||||||
|
_mask = QImage(wanted, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
_mask.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
_mask.fill(Qt::transparent);
|
||||||
|
if (icon.isValid()) {
|
||||||
|
auto p = QPainter(&_mask);
|
||||||
|
icon.render(&p, rect());
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
_mask = Images::Colored(std::move(_mask), Qt::white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_image.isNull()) {
|
||||||
|
_image = style::colorizeImage(_mask, st::menuIconColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool PeerMatchesTypes(
|
bool PeerMatchesTypes(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
|
@ -427,11 +436,14 @@ struct AttachWebView::Context {
|
||||||
Dialogs::EntryState dialogsEntryState;
|
Dialogs::EntryState dialogsEntryState;
|
||||||
Api::SendAction action;
|
Api::SendAction action;
|
||||||
bool fromSwitch = false;
|
bool fromSwitch = false;
|
||||||
|
bool fromMainMenu = false;
|
||||||
bool fromBotApp = false;
|
bool fromBotApp = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
AttachWebView::AttachWebView(not_null<Main::Session*> session)
|
AttachWebView::AttachWebView(not_null<Main::Session*> session)
|
||||||
: _session(session) {
|
: _session(session)
|
||||||
|
, _refreshTimer([=] { requestBots(); }) {
|
||||||
|
_refreshTimer.callEach(kRefreshBotsTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachWebView::~AttachWebView() {
|
AttachWebView::~AttachWebView() {
|
||||||
|
@ -526,6 +538,7 @@ void AttachWebView::botHandleMenuButton(Ui::BotWebView::MenuButton button) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button::RemoveFromMenu:
|
case Button::RemoveFromMenu:
|
||||||
|
case Button::RemoveFromMainMenu:
|
||||||
const auto attached = ranges::find(
|
const auto attached = ranges::find(
|
||||||
_attachBots,
|
_attachBots,
|
||||||
not_null{ _bot },
|
not_null{ _bot },
|
||||||
|
@ -540,12 +553,15 @@ void AttachWebView::botHandleMenuButton(Ui::BotWebView::MenuButton button) {
|
||||||
active->activate();
|
active->activate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const auto main = (button == Button::RemoveFromMainMenu);
|
||||||
_panel->showBox(Ui::MakeConfirmBox({
|
_panel->showBox(Ui::MakeConfirmBox({
|
||||||
tr::lng_bot_remove_from_menu_sure(
|
(main
|
||||||
tr::now,
|
? tr::lng_bot_remove_from_side_menu_sure
|
||||||
lt_bot,
|
: tr::lng_bot_remove_from_menu_sure)(
|
||||||
Ui::Text::Bold(name),
|
tr::now,
|
||||||
Ui::Text::WithEntities),
|
lt_bot,
|
||||||
|
Ui::Text::Bold(name),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
done,
|
done,
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
|
@ -556,6 +572,7 @@ void AttachWebView::botSendData(QByteArray data) {
|
||||||
if (!_context
|
if (!_context
|
||||||
|| _context->fromSwitch
|
|| _context->fromSwitch
|
||||||
|| _context->fromBotApp
|
|| _context->fromBotApp
|
||||||
|
|| _context->fromMainMenu
|
||||||
|| _context->action.history->peer != _bot
|
|| _context->action.history->peer != _bot
|
||||||
|| _lastShownQueryId) {
|
|| _lastShownQueryId) {
|
||||||
return;
|
return;
|
||||||
|
@ -687,6 +704,7 @@ bool AttachWebView::IsSame(
|
||||||
&& (a->controller == b.controller)
|
&& (a->controller == b.controller)
|
||||||
&& (a->dialogsEntryState == b.dialogsEntryState)
|
&& (a->dialogsEntryState == b.dialogsEntryState)
|
||||||
&& (a->fromSwitch == b.fromSwitch)
|
&& (a->fromSwitch == b.fromSwitch)
|
||||||
|
&& (a->fromMainMenu == b.fromMainMenu)
|
||||||
&& (a->action.history == b.action.history)
|
&& (a->action.history == b.action.history)
|
||||||
&& (a->action.replyTo == b.action.replyTo)
|
&& (a->action.replyTo == b.action.replyTo)
|
||||||
&& (a->action.options.sendAs == b.action.options.sendAs)
|
&& (a->action.options.sendAs == b.action.options.sendAs)
|
||||||
|
@ -702,7 +720,7 @@ void AttachWebView::request(
|
||||||
bot,
|
bot,
|
||||||
button,
|
button,
|
||||||
LookupContext(controller, action),
|
LookupContext(controller, action),
|
||||||
button.fromMenu ? nullptr : controller.get());
|
button.fromAttachMenu ? nullptr : controller.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachWebView::requestWithOptionalConfirm(
|
void AttachWebView::requestWithOptionalConfirm(
|
||||||
|
@ -762,7 +780,7 @@ void AttachWebView::request(const WebViewButton &button) {
|
||||||
data.vquery_id().v,
|
data.vquery_id().v,
|
||||||
qs(data.vurl()),
|
qs(data.vurl()),
|
||||||
button.text,
|
button.text,
|
||||||
button.fromMenu || button.url.isEmpty());
|
button.fromAttachMenu || button.url.isEmpty());
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
if (error.type() == u"BOT_INVALID"_q) {
|
if (error.type() == u"BOT_INVALID"_q) {
|
||||||
|
@ -800,13 +818,11 @@ void AttachWebView::requestBots() {
|
||||||
_attachBots.reserve(data.vbots().v.size());
|
_attachBots.reserve(data.vbots().v.size());
|
||||||
for (const auto &bot : data.vbots().v) {
|
for (const auto &bot : data.vbots().v) {
|
||||||
if (auto parsed = ParseAttachBot(_session, bot)) {
|
if (auto parsed = ParseAttachBot(_session, bot)) {
|
||||||
if (!parsed->inactive) {
|
if (const auto icon = parsed->icon) {
|
||||||
if (const auto icon = parsed->icon) {
|
parsed->media = icon->createMediaView();
|
||||||
parsed->media = icon->createMediaView();
|
icon->save(Data::FileOrigin(), {});
|
||||||
icon->save(Data::FileOrigin(), {});
|
|
||||||
}
|
|
||||||
_attachBots.push_back(std::move(*parsed));
|
|
||||||
}
|
}
|
||||||
|
_attachBots.push_back(std::move(*parsed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_attachBotsUpdates.fire({});
|
_attachBotsUpdates.fire({});
|
||||||
|
@ -818,13 +834,13 @@ void AttachWebView::requestBots() {
|
||||||
|
|
||||||
void AttachWebView::requestAddToMenu(
|
void AttachWebView::requestAddToMenu(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
const QString &startCommand) {
|
std::optional<QString> startCommand) {
|
||||||
requestAddToMenu(bot, startCommand, nullptr, std::nullopt, PeerTypes());
|
requestAddToMenu(bot, startCommand, nullptr, std::nullopt, PeerTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachWebView::requestAddToMenu(
|
void AttachWebView::requestAddToMenu(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
const QString &startCommand,
|
std::optional<QString> startCommand,
|
||||||
Window::SessionController *controller,
|
Window::SessionController *controller,
|
||||||
std::optional<Api::SendAction> action,
|
std::optional<Api::SendAction> action,
|
||||||
PeerTypes chooseTypes) {
|
PeerTypes chooseTypes) {
|
||||||
|
@ -863,16 +879,22 @@ void AttachWebView::requestAddToMenu(
|
||||||
const auto open = [=](PeerTypes types) {
|
const auto open = [=](PeerTypes types) {
|
||||||
const auto strong = chooseController.get();
|
const auto strong = chooseController.get();
|
||||||
if (!strong) {
|
if (!strong) {
|
||||||
if (wasController) {
|
if (wasController || !startCommand) {
|
||||||
// Just ignore the click if controller was destroyed.
|
// Just ignore the click if controller was destroyed.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (!startCommand) {
|
||||||
|
_bot = bot;
|
||||||
|
acceptDisclaimer(strong, [=] {
|
||||||
|
requestSimple(strong, bot, { .fromMainMenu = true });
|
||||||
|
});
|
||||||
|
return true;
|
||||||
} else if (const auto useTypes = chooseTypes & types) {
|
} else if (const auto useTypes = chooseTypes & types) {
|
||||||
const auto done = [=](not_null<Data::Thread*> thread) {
|
const auto done = [=](not_null<Data::Thread*> thread) {
|
||||||
strong->showThread(thread);
|
strong->showThread(thread);
|
||||||
requestWithOptionalConfirm(
|
requestWithOptionalConfirm(
|
||||||
bot,
|
bot,
|
||||||
{ .startCommand = startCommand },
|
{ .startCommand = *startCommand },
|
||||||
LookupContext(strong, Api::SendAction(thread)));
|
LookupContext(strong, Api::SendAction(thread)));
|
||||||
};
|
};
|
||||||
ShowChooseBox(strong, useTypes, done);
|
ShowChooseBox(strong, useTypes, done);
|
||||||
|
@ -883,7 +905,7 @@ void AttachWebView::requestAddToMenu(
|
||||||
}
|
}
|
||||||
requestWithOptionalConfirm(
|
requestWithOptionalConfirm(
|
||||||
bot,
|
bot,
|
||||||
{ .startCommand = startCommand },
|
{ .startCommand = *startCommand },
|
||||||
*context);
|
*context);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -891,6 +913,14 @@ void AttachWebView::requestAddToMenu(
|
||||||
_session->data().processUsers(data.vusers());
|
_session->data().processUsers(data.vusers());
|
||||||
if (const auto parsed = ParseAttachBot(_session, data.vbot())) {
|
if (const auto parsed = ParseAttachBot(_session, data.vbot())) {
|
||||||
if (bot == parsed->user) {
|
if (bot == parsed->user) {
|
||||||
|
const auto i = ranges::find(
|
||||||
|
_attachBots,
|
||||||
|
not_null(bot),
|
||||||
|
&AttachWebViewBot::user);
|
||||||
|
if (i != end(_attachBots)) {
|
||||||
|
// Save flags in our list, like 'inactive'.
|
||||||
|
*i = *parsed;
|
||||||
|
}
|
||||||
const auto types = parsed->types;
|
const auto types = parsed->types;
|
||||||
if (parsed->inactive) {
|
if (parsed->inactive) {
|
||||||
confirmAddToMenu(*parsed, [=] {
|
confirmAddToMenu(*parsed, [=] {
|
||||||
|
@ -910,7 +940,7 @@ void AttachWebView::requestAddToMenu(
|
||||||
_addToMenuId = 0;
|
_addToMenuId = 0;
|
||||||
_addToMenuBot = nullptr;
|
_addToMenuBot = nullptr;
|
||||||
_addToMenuContext = nullptr;
|
_addToMenuContext = nullptr;
|
||||||
_addToMenuStartCommand = QString();
|
_addToMenuStartCommand = std::nullopt;
|
||||||
showToast(tr::lng_bot_menu_not_supported(tr::now));
|
showToast(tr::lng_bot_menu_not_supported(tr::now));
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
@ -983,18 +1013,27 @@ void AttachWebView::requestSimple(
|
||||||
controller,
|
controller,
|
||||||
Api::SendAction(bot->owner().history(bot))));
|
Api::SendAction(bot->owner().history(bot))));
|
||||||
_context->fromSwitch = button.fromSwitch;
|
_context->fromSwitch = button.fromSwitch;
|
||||||
confirmOpen(controller, [=] {
|
_context->fromMainMenu = button.fromMainMenu;
|
||||||
requestSimple(button);
|
if (button.fromMainMenu) {
|
||||||
});
|
acceptDisclaimer(controller, [=] {
|
||||||
|
requestSimple(button);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
confirmOpen(controller, [=] {
|
||||||
|
requestSimple(button);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachWebView::requestSimple(const WebViewButton &button) {
|
void AttachWebView::requestSimple(const WebViewButton &button) {
|
||||||
using Flag = MTPmessages_RequestSimpleWebView::Flag;
|
using Flag = MTPmessages_RequestSimpleWebView::Flag;
|
||||||
_requestId = _session->api().request(MTPmessages_RequestSimpleWebView(
|
_requestId = _session->api().request(MTPmessages_RequestSimpleWebView(
|
||||||
MTP_flags(Flag::f_theme_params
|
MTP_flags(Flag::f_theme_params
|
||||||
|
| (button.fromMainMenu ? Flag::f_from_side_menu : Flag::f_url)
|
||||||
| (button.fromSwitch ? Flag::f_from_switch_webview : Flag())),
|
| (button.fromSwitch ? Flag::f_from_switch_webview : Flag())),
|
||||||
_bot->inputUser,
|
_bot->inputUser,
|
||||||
MTP_bytes(button.url),
|
MTP_bytes(button.url),
|
||||||
|
MTP_string(""), // start_param
|
||||||
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)),
|
MTP_dataJSON(MTP_bytes(Window::Theme::WebViewParams().json)),
|
||||||
MTP_string("tdesktop")
|
MTP_string("tdesktop")
|
||||||
)).done([=](const MTPSimpleWebViewResult &result) {
|
)).done([=](const MTPSimpleWebViewResult &result) {
|
||||||
|
@ -1200,6 +1239,112 @@ void AttachWebView::confirmOpen(
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AttachWebView::acceptDisclaimer(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Fn<void()> done) {
|
||||||
|
const auto local = _bot ? &_bot->session().local() : nullptr;
|
||||||
|
if (!local) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto i = ranges::find(
|
||||||
|
_attachBots,
|
||||||
|
not_null(_bot),
|
||||||
|
&AttachWebViewBot::user);
|
||||||
|
if (i == end(_attachBots)) {
|
||||||
|
_attachBotsUpdates.fire({});
|
||||||
|
return;
|
||||||
|
} else if (i->inactive) {
|
||||||
|
requestAddToMenu(_bot, {}, controller, {}, {});
|
||||||
|
return;
|
||||||
|
} else if (!i->disclaimerRequired) {
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto weak = base::make_weak(this);
|
||||||
|
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
const auto updateCheck = std::make_shared<Fn<void()>>();
|
||||||
|
const auto validateCheck = std::make_shared<Fn<bool()>>();
|
||||||
|
|
||||||
|
const auto callback = [=](Fn<void()> close) {
|
||||||
|
if (validateCheck && (*validateCheck)() && weak) {
|
||||||
|
const auto i = ranges::find(
|
||||||
|
_attachBots,
|
||||||
|
not_null(_bot),
|
||||||
|
&AttachWebViewBot::user);
|
||||||
|
if (i == end(_attachBots)) {
|
||||||
|
_attachBotsUpdates.fire({});
|
||||||
|
} else if (i->inactive) {
|
||||||
|
requestAddToMenu(_bot, std::nullopt);
|
||||||
|
} else {
|
||||||
|
i->disclaimerRequired = false;
|
||||||
|
requestBots();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ui::ConfirmBox(box, {
|
||||||
|
.text = tr::lng_mini_apps_disclaimer_text(
|
||||||
|
tr::now,
|
||||||
|
Ui::Text::RichLangValue),
|
||||||
|
.confirmed = callback,
|
||||||
|
.confirmText = tr::lng_box_ok(),
|
||||||
|
.title = tr::lng_mini_apps_disclaimer_title(),
|
||||||
|
});
|
||||||
|
|
||||||
|
auto checkView = std::make_unique<Ui::CheckView>(
|
||||||
|
st::defaultCheck,
|
||||||
|
false,
|
||||||
|
[=] { if (*updateCheck) { (*updateCheck)(); } });
|
||||||
|
const auto check = checkView.get();
|
||||||
|
const auto row = box->addRow(
|
||||||
|
object_ptr<Ui::Checkbox>(
|
||||||
|
box.get(),
|
||||||
|
tr::lng_mini_apps_disclaimer_button(
|
||||||
|
lt_link,
|
||||||
|
rpl::single(Ui::Text::Link(
|
||||||
|
tr::lng_mini_apps_disclaimer_link(tr::now),
|
||||||
|
tr::lng_mini_apps_tos_url(tr::now))),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
|
st::defaultBoxCheckbox,
|
||||||
|
std::move(checkView)),
|
||||||
|
{
|
||||||
|
st::boxRowPadding.left(),
|
||||||
|
st::boxRowPadding.left(),
|
||||||
|
st::boxRowPadding.right(),
|
||||||
|
st::defaultBoxCheckbox.margin.bottom(),
|
||||||
|
});
|
||||||
|
row->setAllowTextLines(5);
|
||||||
|
row->setClickHandlerFilter([=](
|
||||||
|
const ClickHandlerPtr &link,
|
||||||
|
Qt::MouseButton button) {
|
||||||
|
ActivateClickHandler(row, link, ClickContext{
|
||||||
|
.button = button,
|
||||||
|
.other = QVariant::fromValue(ClickHandlerContext{
|
||||||
|
.show = box->uiShow(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
(*updateCheck) = [=] { row->update(); };
|
||||||
|
|
||||||
|
const auto showError = Ui::CheckView::PrepareNonToggledError(
|
||||||
|
check,
|
||||||
|
box->lifetime());
|
||||||
|
|
||||||
|
(*validateCheck) = [=] {
|
||||||
|
if (check->checked()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
showError();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void AttachWebView::ClearAll() {
|
void AttachWebView::ClearAll() {
|
||||||
while (!ActiveWebViews().empty()) {
|
while (!ActiveWebViews().empty()) {
|
||||||
ActiveWebViews().front()->cancel();
|
ActiveWebViews().front()->cancel();
|
||||||
|
@ -1227,10 +1372,14 @@ void AttachWebView::show(
|
||||||
const auto hasOpenBot = !_context
|
const auto hasOpenBot = !_context
|
||||||
|| (_bot != _context->action.history->peer);
|
|| (_bot != _context->action.history->peer);
|
||||||
const auto hasRemoveFromMenu = (attached != end(_attachBots))
|
const auto hasRemoveFromMenu = (attached != end(_attachBots))
|
||||||
&& !attached->inactive;
|
&& (!attached->inactive || attached->inMainMenu);
|
||||||
const auto buttons = (hasSettings ? Button::Settings : Button::None)
|
const auto buttons = (hasSettings ? Button::Settings : Button::None)
|
||||||
| (hasOpenBot ? Button::OpenBot : Button::None)
|
| (hasOpenBot ? Button::OpenBot : Button::None)
|
||||||
| (hasRemoveFromMenu ? Button::RemoveFromMenu : Button::None);
|
| (!hasRemoveFromMenu
|
||||||
|
? Button::None
|
||||||
|
: attached->inMainMenu
|
||||||
|
? Button::RemoveFromMainMenu
|
||||||
|
: Button::RemoveFromMenu);
|
||||||
|
|
||||||
_lastShownUrl = url;
|
_lastShownUrl = url;
|
||||||
_lastShownQueryId = queryId;
|
_lastShownQueryId = queryId;
|
||||||
|
@ -1318,16 +1467,20 @@ void AttachWebView::confirmAddToMenu(
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
showToast(tr::lng_bot_add_to_menu_done(tr::now));
|
showToast((bot.inMainMenu
|
||||||
|
? tr::lng_bot_add_to_side_menu_done
|
||||||
|
: tr::lng_bot_add_to_menu_done)(tr::now));
|
||||||
});
|
});
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
Ui::ConfirmBox(box, {
|
Ui::ConfirmBox(box, {
|
||||||
tr::lng_bot_add_to_menu(
|
(bot.inMainMenu
|
||||||
tr::now,
|
? tr::lng_bot_add_to_side_menu
|
||||||
lt_bot,
|
: tr::lng_bot_add_to_menu)(
|
||||||
Ui::Text::Bold(bot.name),
|
tr::now,
|
||||||
Ui::Text::WithEntities),
|
lt_bot,
|
||||||
|
Ui::Text::Bold(bot.name),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
done,
|
done,
|
||||||
});
|
});
|
||||||
if (bot.requestWriteAccess) {
|
if (bot.requestWriteAccess) {
|
||||||
|
@ -1406,7 +1559,8 @@ std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
|
||||||
}, &st::menuIconFile);
|
}, &st::menuIconFile);
|
||||||
}
|
}
|
||||||
for (const auto &bot : bots->attachBots()) {
|
for (const auto &bot : bots->attachBots()) {
|
||||||
if (!PeerMatchesTypes(peer, bot.user, bot.types)) {
|
if (!bot.inAttachMenu
|
||||||
|
|| !PeerMatchesTypes(peer, bot.user, bot.types)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto callback = [=] {
|
const auto callback = [=] {
|
||||||
|
@ -1414,7 +1568,7 @@ std::unique_ptr<Ui::DropdownMenu> MakeAttachBotsMenu(
|
||||||
controller,
|
controller,
|
||||||
actionFactory(),
|
actionFactory(),
|
||||||
bot.user,
|
bot.user,
|
||||||
{ .fromMenu = true });
|
{ .fromAttachMenu = true });
|
||||||
};
|
};
|
||||||
auto action = base::make_unique_q<BotAction>(
|
auto action = base::make_unique_q<BotAction>(
|
||||||
raw,
|
raw,
|
||||||
|
|
|
@ -7,10 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/weak_ptr.h"
|
|
||||||
#include "base/flags.h"
|
#include "base/flags.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
#include "ui/chat/attach/attach_bot_webview.h"
|
#include "ui/chat/attach/attach_bot_webview.h"
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
struct SendAction;
|
struct SendAction;
|
||||||
|
@ -60,9 +62,12 @@ struct AttachWebViewBot {
|
||||||
std::shared_ptr<Data::DocumentMedia> media;
|
std::shared_ptr<Data::DocumentMedia> media;
|
||||||
QString name;
|
QString name;
|
||||||
PeerTypes types = 0;
|
PeerTypes types = 0;
|
||||||
bool inactive = false;
|
bool inactive : 1 = false;
|
||||||
bool hasSettings = false;
|
bool inMainMenu : 1 = false;
|
||||||
bool requestWriteAccess = false;
|
bool inAttachMenu : 1 = false;
|
||||||
|
bool disclaimerRequired : 1 = false;
|
||||||
|
bool hasSettings : 1 = false;
|
||||||
|
bool requestWriteAccess : 1 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AttachWebView final
|
class AttachWebView final
|
||||||
|
@ -76,7 +81,8 @@ public:
|
||||||
QString text;
|
QString text;
|
||||||
QString startCommand;
|
QString startCommand;
|
||||||
QByteArray url;
|
QByteArray url;
|
||||||
bool fromMenu = false;
|
bool fromAttachMenu = false;
|
||||||
|
bool fromMainMenu = false;
|
||||||
bool fromSwitch = false;
|
bool fromSwitch = false;
|
||||||
};
|
};
|
||||||
void request(
|
void request(
|
||||||
|
@ -116,10 +122,10 @@ public:
|
||||||
|
|
||||||
void requestAddToMenu(
|
void requestAddToMenu(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
const QString &startCommand);
|
std::optional<QString> startCommand);
|
||||||
void requestAddToMenu(
|
void requestAddToMenu(
|
||||||
not_null<UserData*> bot,
|
not_null<UserData*> bot,
|
||||||
const QString &startCommand,
|
std::optional<QString> startCommand,
|
||||||
Window::SessionController *controller,
|
Window::SessionController *controller,
|
||||||
std::optional<Api::SendAction> action,
|
std::optional<Api::SendAction> action,
|
||||||
PeerTypes chooseTypes);
|
PeerTypes chooseTypes);
|
||||||
|
@ -171,6 +177,9 @@ private:
|
||||||
void confirmOpen(
|
void confirmOpen(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
Fn<void()> done);
|
Fn<void()> done);
|
||||||
|
void acceptDisclaimer(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Fn<void()> done);
|
||||||
|
|
||||||
enum class ToggledState {
|
enum class ToggledState {
|
||||||
Removed,
|
Removed,
|
||||||
|
@ -200,6 +209,8 @@ private:
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
|
||||||
|
base::Timer _refreshTimer;
|
||||||
|
|
||||||
std::unique_ptr<Context> _context;
|
std::unique_ptr<Context> _context;
|
||||||
std::unique_ptr<Context> _lastShownContext;
|
std::unique_ptr<Context> _lastShownContext;
|
||||||
QString _lastShownUrl;
|
QString _lastShownUrl;
|
||||||
|
@ -221,7 +232,7 @@ private:
|
||||||
std::unique_ptr<Context> _addToMenuContext;
|
std::unique_ptr<Context> _addToMenuContext;
|
||||||
UserData *_addToMenuBot = nullptr;
|
UserData *_addToMenuBot = nullptr;
|
||||||
mtpRequestId _addToMenuId = 0;
|
mtpRequestId _addToMenuId = 0;
|
||||||
QString _addToMenuStartCommand;
|
std::optional<QString> _addToMenuStartCommand;
|
||||||
base::weak_ptr<Window::SessionController> _addToMenuChooseController;
|
base::weak_ptr<Window::SessionController> _addToMenuChooseController;
|
||||||
PeerTypes _addToMenuChooseTypes;
|
PeerTypes _addToMenuChooseTypes;
|
||||||
|
|
||||||
|
@ -239,4 +250,21 @@ private:
|
||||||
Fn<Api::SendAction()> actionFactory,
|
Fn<Api::SendAction()> actionFactory,
|
||||||
Fn<void(bool)> attach);
|
Fn<void(bool)> attach);
|
||||||
|
|
||||||
|
class MenuBotIcon final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
MenuBotIcon(
|
||||||
|
QWidget *parent,
|
||||||
|
std::shared_ptr<Data::DocumentMedia> media);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
void validate();
|
||||||
|
|
||||||
|
std::shared_ptr<Data::DocumentMedia> _media;
|
||||||
|
QImage _image;
|
||||||
|
QImage _mask;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace InlineBots
|
} // namespace InlineBots
|
||||||
|
|
|
@ -735,41 +735,9 @@ void Panel::requestTermsAcceptance(
|
||||||
|
|
||||||
(*update) = [=] { row->update(); };
|
(*update) = [=] { row->update(); };
|
||||||
|
|
||||||
struct State {
|
const auto showError = Ui::CheckView::PrepareNonToggledError(
|
||||||
bool error = false;
|
check,
|
||||||
Ui::Animations::Simple errorAnimation;
|
box->lifetime());
|
||||||
};
|
|
||||||
const auto state = box->lifetime().make_state<State>();
|
|
||||||
const auto showError = [=] {
|
|
||||||
const auto callback = [=] {
|
|
||||||
const auto error = state->errorAnimation.value(
|
|
||||||
state->error ? 1. : 0.);
|
|
||||||
if (error == 0.) {
|
|
||||||
check->setUntoggledOverride(std::nullopt);
|
|
||||||
} else {
|
|
||||||
const auto color = anim::color(
|
|
||||||
st::defaultCheck.untoggledFg,
|
|
||||||
st::boxTextFgError,
|
|
||||||
error);
|
|
||||||
check->setUntoggledOverride(color);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
state->error = true;
|
|
||||||
state->errorAnimation.stop();
|
|
||||||
state->errorAnimation.start(
|
|
||||||
callback,
|
|
||||||
0.,
|
|
||||||
1.,
|
|
||||||
st::defaultCheck.duration);
|
|
||||||
};
|
|
||||||
|
|
||||||
row->checkedChanges(
|
|
||||||
) | rpl::filter([=](bool checked) {
|
|
||||||
return checked;
|
|
||||||
}) | rpl::start_with_next([=] {
|
|
||||||
state->error = false;
|
|
||||||
check->setUntoggledOverride(std::nullopt);
|
|
||||||
}, row->lifetime());
|
|
||||||
|
|
||||||
box->addButton(tr::lng_payments_terms_accept(), [=] {
|
box->addButton(tr::lng_payments_terms_accept(), [=] {
|
||||||
if (check->checked()) {
|
if (check->checked()) {
|
||||||
|
|
|
@ -2808,7 +2808,13 @@ void Account::writeTrustedBots() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::readTrustedBots() {
|
void Account::readTrustedBots() {
|
||||||
if (!_trustedBotsKey) return;
|
if (_trustedBotsRead) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_trustedBotsRead = true;
|
||||||
|
if (!_trustedBotsKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FileReadDescriptor trusted;
|
FileReadDescriptor trusted;
|
||||||
if (!ReadEncryptedFile(trusted, _trustedBotsKey, _basePath, _localKey)) {
|
if (!ReadEncryptedFile(trusted, _trustedBotsKey, _basePath, _localKey)) {
|
||||||
|
@ -2845,10 +2851,7 @@ void Account::markBotTrustedOpenGame(PeerId botId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Account::isBotTrustedOpenGame(PeerId botId) {
|
bool Account::isBotTrustedOpenGame(PeerId botId) {
|
||||||
if (!_trustedBotsRead) {
|
readTrustedBots();
|
||||||
readTrustedBots();
|
|
||||||
_trustedBotsRead = true;
|
|
||||||
}
|
|
||||||
const auto i = _trustedBots.find(botId);
|
const auto i = _trustedBots.find(botId);
|
||||||
return (i != end(_trustedBots))
|
return (i != end(_trustedBots))
|
||||||
&& ((i->second & BotTrustFlag::NoOpenGame) == 0);
|
&& ((i->second & BotTrustFlag::NoOpenGame) == 0);
|
||||||
|
@ -2870,10 +2873,7 @@ void Account::markBotTrustedPayment(PeerId botId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Account::isBotTrustedPayment(PeerId botId) {
|
bool Account::isBotTrustedPayment(PeerId botId) {
|
||||||
if (!_trustedBotsRead) {
|
readTrustedBots();
|
||||||
readTrustedBots();
|
|
||||||
_trustedBotsRead = true;
|
|
||||||
}
|
|
||||||
const auto i = _trustedBots.find(botId);
|
const auto i = _trustedBots.find(botId);
|
||||||
return (i != end(_trustedBots))
|
return (i != end(_trustedBots))
|
||||||
&& ((i->second & BotTrustFlag::Payment) != 0);
|
&& ((i->second & BotTrustFlag::Payment) != 0);
|
||||||
|
@ -2895,10 +2895,7 @@ void Account::markBotTrustedOpenWebView(PeerId botId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Account::isBotTrustedOpenWebView(PeerId botId) {
|
bool Account::isBotTrustedOpenWebView(PeerId botId) {
|
||||||
if (!_trustedBotsRead) {
|
readTrustedBots();
|
||||||
readTrustedBots();
|
|
||||||
_trustedBotsRead = true;
|
|
||||||
}
|
|
||||||
const auto i = _trustedBots.find(botId);
|
const auto i = _trustedBots.find(botId);
|
||||||
return (i != end(_trustedBots))
|
return (i != end(_trustedBots))
|
||||||
&& ((i->second & BotTrustFlag::OpenWebView) != 0);
|
&& ((i->second & BotTrustFlag::OpenWebView) != 0);
|
||||||
|
|
|
@ -184,9 +184,9 @@ private:
|
||||||
Failed,
|
Failed,
|
||||||
};
|
};
|
||||||
enum class BotTrustFlag : uchar {
|
enum class BotTrustFlag : uchar {
|
||||||
NoOpenGame = (1 << 0),
|
NoOpenGame = (1 << 0),
|
||||||
Payment = (1 << 1),
|
Payment = (1 << 1),
|
||||||
OpenWebView = (1 << 2),
|
OpenWebView = (1 << 2),
|
||||||
};
|
};
|
||||||
friend inline constexpr bool is_flag_type(BotTrustFlag) { return true; };
|
friend inline constexpr bool is_flag_type(BotTrustFlag) { return true; };
|
||||||
|
|
||||||
|
|
|
@ -535,12 +535,17 @@ bool Panel::showWebview(
|
||||||
callback(tr::lng_bot_reload_page(tr::now), [=] {
|
callback(tr::lng_bot_reload_page(tr::now), [=] {
|
||||||
_webview->window.reload();
|
_webview->window.reload();
|
||||||
}, &st::menuIconRestore);
|
}, &st::menuIconRestore);
|
||||||
if (_menuButtons & MenuButton::RemoveFromMenu) {
|
const auto main = (_menuButtons & MenuButton::RemoveFromMainMenu);
|
||||||
|
if (main || (_menuButtons & MenuButton::RemoveFromMenu)) {
|
||||||
const auto handler = [=] {
|
const auto handler = [=] {
|
||||||
_delegate->botHandleMenuButton(MenuButton::RemoveFromMenu);
|
_delegate->botHandleMenuButton(main
|
||||||
|
? MenuButton::RemoveFromMainMenu
|
||||||
|
: MenuButton::RemoveFromMenu);
|
||||||
};
|
};
|
||||||
callback({
|
callback({
|
||||||
.text = tr::lng_bot_remove_from_menu(tr::now),
|
.text = (main
|
||||||
|
? tr::lng_bot_remove_from_side_menu
|
||||||
|
: tr::lng_bot_remove_from_menu)(tr::now),
|
||||||
.handler = handler,
|
.handler = handler,
|
||||||
.icon = &st::menuIconDeleteAttention,
|
.icon = &st::menuIconDeleteAttention,
|
||||||
.isAttention = true,
|
.isAttention = true,
|
||||||
|
|
|
@ -36,10 +36,11 @@ struct MainButtonArgs {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class MenuButton {
|
enum class MenuButton {
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
Settings = 0x01,
|
Settings = 0x01,
|
||||||
OpenBot = 0x02,
|
OpenBot = 0x02,
|
||||||
RemoveFromMenu = 0x04,
|
RemoveFromMenu = 0x04,
|
||||||
|
RemoveFromMainMenu = 0x08,
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(MenuButton) { return true; }
|
inline constexpr bool is_flag_type(MenuButton) { return true; }
|
||||||
using MenuButtons = base::flags<MenuButton>;
|
using MenuButtons = base::flags<MenuButton>;
|
||||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/empty_userpic.h"
|
#include "ui/empty_userpic.h"
|
||||||
#include "ui/unread_badge_paint.h"
|
#include "ui/unread_badge_paint.h"
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
|
#include "inline_bots/bot_attach_web_view.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
|
@ -198,6 +199,45 @@ void ShowCallsBox(not_null<Window::SessionController*> window) {
|
||||||
}) | rpl::flatten_latest();
|
}) | rpl::flatten_latest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupMenuBots(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller) {
|
||||||
|
const auto wrap = container->add(
|
||||||
|
object_ptr<Ui::VerticalLayout>(container));
|
||||||
|
const auto bots = &controller->session().attachWebView();
|
||||||
|
|
||||||
|
rpl::single(
|
||||||
|
rpl::empty
|
||||||
|
) | rpl::then(
|
||||||
|
bots->attachBotsUpdates()
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
wrap->clear();
|
||||||
|
for (const auto &bot : bots->attachBots()) {
|
||||||
|
if (!bot.inMainMenu) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto button = Settings::AddButton(
|
||||||
|
container,
|
||||||
|
rpl::single(bot.name),
|
||||||
|
st::mainMenuButton);
|
||||||
|
const auto icon = Ui::CreateChild<InlineBots::MenuBotIcon>(
|
||||||
|
button.get(),
|
||||||
|
bot.media);
|
||||||
|
button->heightValue(
|
||||||
|
) | rpl::start_with_next([=](int height) {
|
||||||
|
icon->move(
|
||||||
|
st::mainMenuButton.iconLeft,
|
||||||
|
(height - icon->height()) / 2);
|
||||||
|
}, button->lifetime());
|
||||||
|
button->setClickedCallback([=] {
|
||||||
|
bots->requestSimple(controller, bot.user, {
|
||||||
|
.fromMainMenu = true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, wrap->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class MainMenu::ToggleAccountsButton final : public Ui::AbstractButton {
|
class MainMenu::ToggleAccountsButton final : public Ui::AbstractButton {
|
||||||
|
@ -784,6 +824,8 @@ void MainMenu::setupMenu() {
|
||||||
Info::Stories::Make(controller->session().user()));
|
Info::Stories::Make(controller->session().user()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
SetupMenuBots(_menu, controller);
|
||||||
|
|
||||||
addAction(
|
addAction(
|
||||||
tr::lng_menu_contacts(),
|
tr::lng_menu_contacts(),
|
||||||
{ &st::menuIconProfile }
|
{ &st::menuIconProfile }
|
||||||
|
|
|
@ -596,6 +596,13 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||||
contextUser->owner().history(contextUser))
|
contextUser->owner().history(contextUser))
|
||||||
: std::optional<Api::SendAction>()),
|
: std::optional<Api::SendAction>()),
|
||||||
info.attachBotChooseTypes);
|
info.attachBotChooseTypes);
|
||||||
|
} else if (bot && info.attachBotMenuOpen) {
|
||||||
|
bot->session().attachWebView().requestAddToMenu(
|
||||||
|
bot,
|
||||||
|
std::nullopt,
|
||||||
|
parentController(),
|
||||||
|
std::optional<Api::SendAction>(),
|
||||||
|
{});
|
||||||
} else {
|
} else {
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
showPeerHistory(peer, params, msgId);
|
showPeerHistory(peer, params, msgId);
|
||||||
|
|
|
@ -212,6 +212,7 @@ public:
|
||||||
bool botAppForceConfirmation = false;
|
bool botAppForceConfirmation = false;
|
||||||
QString attachBotUsername;
|
QString attachBotUsername;
|
||||||
std::optional<QString> attachBotToggleCommand;
|
std::optional<QString> attachBotToggleCommand;
|
||||||
|
bool attachBotMenuOpen = false;
|
||||||
InlineBots::PeerTypes attachBotChooseTypes;
|
InlineBots::PeerTypes attachBotChooseTypes;
|
||||||
std::optional<QString> voicechatHash;
|
std::optional<QString> voicechatHash;
|
||||||
FullMsgId clickFromMessageId;
|
FullMsgId clickFromMessageId;
|
||||||
|
|
Loading…
Add table
Reference in a new issue