mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Prepared messages sharing from miniapp.
This commit is contained in:
parent
3d77bff0c9
commit
2d1fb0562d
20 changed files with 465 additions and 63 deletions
|
@ -1040,6 +1040,8 @@ PRIVATE
|
|||
info/info_wrap_widget.h
|
||||
inline_bots/bot_attach_web_view.cpp
|
||||
inline_bots/bot_attach_web_view.h
|
||||
inline_bots/inline_bot_confirm_prepared.cpp
|
||||
inline_bots/inline_bot_confirm_prepared.h
|
||||
inline_bots/inline_bot_layout_internal.cpp
|
||||
inline_bots/inline_bot_layout_internal.h
|
||||
inline_bots/inline_bot_layout_item.cpp
|
||||
|
|
|
@ -3425,6 +3425,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_bot_emoji_status_text" = "Do you want to set this emoji status suggested by {bot}?";
|
||||
"lng_bot_emoji_status_access_text" = "{bot} requests access to set your **emoji status**. You will be able to revoke this access in the profile page of {name}.";
|
||||
"lng_bot_emoji_status_access_allow" = "Allow";
|
||||
"lng_bot_share_prepared_title" = "Share Message";
|
||||
"lng_bot_share_prepared_about" = "{bot} mini app suggests you to send this message to a chat you select.";
|
||||
"lng_bot_share_prepared_button" = "Share With...";
|
||||
|
||||
"lng_bot_status_users#one" = "{count} monthly user";
|
||||
"lng_bot_status_users#other" = "{count} monthly users";
|
||||
|
|
|
@ -3947,7 +3947,8 @@ void ApiWrap::sendInlineResult(
|
|||
not_null<UserData*> bot,
|
||||
not_null<InlineBots::Result*> data,
|
||||
const SendAction &action,
|
||||
std::optional<MsgId> localMessageId) {
|
||||
std::optional<MsgId> localMessageId,
|
||||
Fn<void(bool)> done) {
|
||||
sendAction(action);
|
||||
|
||||
const auto history = action.history;
|
||||
|
@ -4027,11 +4028,13 @@ void ApiWrap::sendInlineResult(
|
|||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
done(true);
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
sendMessageFail(error, peer, randomId, newId);
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
done(false);
|
||||
});
|
||||
finishForwarding(action);
|
||||
}
|
||||
|
|
|
@ -361,7 +361,8 @@ public:
|
|||
not_null<UserData*> bot,
|
||||
not_null<InlineBots::Result*> data,
|
||||
const SendAction &action,
|
||||
std::optional<MsgId> localMessageId);
|
||||
std::optional<MsgId> localMessageId,
|
||||
Fn<void(bool)> done = nullptr);
|
||||
void sendMessageFail(
|
||||
const MTP::Error &error,
|
||||
not_null<PeerData*> peer,
|
||||
|
|
|
@ -712,6 +712,14 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
|||
true);
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
not_null<HistoryItem*> item) {
|
||||
Expects(item->history() == this);
|
||||
Expects(item->isLocal());
|
||||
|
||||
return addNewItem(item, true);
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> History::addSponsoredMessage(
|
||||
MsgId id,
|
||||
Data::SponsoredFrom from,
|
||||
|
|
|
@ -180,6 +180,7 @@ public:
|
|||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
HistoryItemCommonFields &&fields,
|
||||
not_null<GameData*> game);
|
||||
not_null<HistoryItem*> addNewLocalMessage(not_null<HistoryItem*> item);
|
||||
|
||||
not_null<HistoryItem*> addSponsoredMessage(
|
||||
MsgId id,
|
||||
|
|
|
@ -14,28 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP(
|
||||
const MTPvector<MTPInlineQueryPeerType> &types) {
|
||||
using namespace InlineBots;
|
||||
auto result = PeerTypes(0);
|
||||
for (const auto &type : types.v) {
|
||||
result |= type.match([&](const MTPDinlineQueryPeerTypePM &data) {
|
||||
return PeerType::User;
|
||||
}, [&](const MTPDinlineQueryPeerTypeChat &data) {
|
||||
return PeerType::Group;
|
||||
}, [&](const MTPDinlineQueryPeerTypeMegagroup &data) {
|
||||
return PeerType::Group;
|
||||
}, [&](const MTPDinlineQueryPeerTypeBroadcast &data) {
|
||||
return PeerType::Broadcast;
|
||||
}, [&](const MTPDinlineQueryPeerTypeBotPM &data) {
|
||||
return PeerType::Bot;
|
||||
}, [&](const MTPDinlineQueryPeerTypeSameBotPM &data) {
|
||||
return PeerType();
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] RequestPeerQuery RequestPeerQueryFromTL(
|
||||
const MTPDkeyboardButtonRequestPeer &query) {
|
||||
using Type = RequestPeerQuery::Type;
|
||||
|
@ -76,6 +54,28 @@ namespace {
|
|||
|
||||
} // namespace
|
||||
|
||||
InlineBots::PeerTypes PeerTypesFromMTP(
|
||||
const MTPvector<MTPInlineQueryPeerType> &types) {
|
||||
using namespace InlineBots;
|
||||
auto result = PeerTypes(0);
|
||||
for (const auto &type : types.v) {
|
||||
result |= type.match([&](const MTPDinlineQueryPeerTypePM &data) {
|
||||
return PeerType::User;
|
||||
}, [&](const MTPDinlineQueryPeerTypeChat &data) {
|
||||
return PeerType::Group;
|
||||
}, [&](const MTPDinlineQueryPeerTypeMegagroup &data) {
|
||||
return PeerType::Group;
|
||||
}, [&](const MTPDinlineQueryPeerTypeBroadcast &data) {
|
||||
return PeerType::Broadcast;
|
||||
}, [&](const MTPDinlineQueryPeerTypeBotPM &data) {
|
||||
return PeerType::Bot;
|
||||
}, [&](const MTPDinlineQueryPeerTypeSameBotPM &data) {
|
||||
return PeerType();
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
HistoryMessageMarkupButton::HistoryMessageMarkupButton(
|
||||
Type type,
|
||||
const QString &text,
|
||||
|
|
|
@ -19,6 +19,9 @@ enum class PeerType : uint8;
|
|||
using PeerTypes = base::flags<PeerType>;
|
||||
} // namespace InlineBots
|
||||
|
||||
[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP(
|
||||
const MTPvector<MTPInlineQueryPeerType> &types);
|
||||
|
||||
enum class ReplyMarkupFlag : uint32 {
|
||||
None = (1U << 0),
|
||||
ForceReply = (1U << 1),
|
||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/qthelp_url.h"
|
||||
#include "base/random.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
|
@ -38,6 +39,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "inline_bots/inline_bot_confirm_prepared.h"
|
||||
#include "iv/iv_instance.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
|
@ -1730,6 +1733,88 @@ void WebViewInstance::botInvokeCustomMethod(
|
|||
}).send();
|
||||
}
|
||||
|
||||
void WebViewInstance::botSendPreparedMessage(
|
||||
Ui::BotWebView::SendPreparedMessageRequest request) {
|
||||
const auto bot = _bot;
|
||||
const auto id = request.id;
|
||||
const auto panel = _panel.get();
|
||||
const auto weak = base::make_weak(panel);
|
||||
const auto callback = request.callback;
|
||||
if (!panel) {
|
||||
callback(u"UNKNOWN_ERROR"_q);
|
||||
return;
|
||||
}
|
||||
_session->api().request(MTPmessages_GetPreparedInlineMessage(
|
||||
bot->inputUser,
|
||||
MTP_string(request.id)
|
||||
)).done([=](const MTPmessages_PreparedInlineMessage &result) {
|
||||
const auto panel = weak.get();
|
||||
const auto &data = result.data();
|
||||
bot->owner().processUsers(data.vusers());
|
||||
const auto parsed = std::shared_ptr<Result>(Result::Create(
|
||||
&bot->session(),
|
||||
data.vquery_id().v,
|
||||
data.vresult()));
|
||||
if (!parsed || !panel) {
|
||||
callback(u"UNKNOWN_ERROR"_q);
|
||||
return;
|
||||
}
|
||||
const auto types = PeerTypesFromMTP(data.vpeer_types());
|
||||
const auto history = bot->owner().history(bot->session().user());
|
||||
const auto item = parsed->makeMessage(history, {
|
||||
.id = bot->owner().nextNonHistoryEntryId(),
|
||||
.flags = MessageFlag::FakeHistoryItem,
|
||||
.from = bot->session().userPeerId(),
|
||||
.date = base::unixtime::now(),
|
||||
.viaBotId = peerToUser(bot->id),
|
||||
});
|
||||
struct State {
|
||||
QPointer<Ui::BoxContent> preview;
|
||||
bool sent = false;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
auto box = Box(PreparedPreviewBox, item, [=] {
|
||||
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();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto box = Window::PrepareChooseRecipientBox(
|
||||
&bot->session(),
|
||||
chosen,
|
||||
tr::lng_inline_switch_choose(),
|
||||
nullptr,
|
||||
types);
|
||||
panel->showBox(std::move(box));
|
||||
});
|
||||
box->boxClosing() | rpl::start_with_next([=] {
|
||||
if (!state->sent) {
|
||||
callback("USER_DECLINED");
|
||||
}
|
||||
}, box->lifetime());
|
||||
state->preview = box.data();
|
||||
panel->showBox(std::move(box));
|
||||
}).fail([=] {
|
||||
callback(u"MESSAGE_EXPIRED"_q);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void WebViewInstance::botSetEmojiStatus(
|
||||
Ui::BotWebView::SetEmojiStatusRequest request) {
|
||||
const auto bot = _bot;
|
||||
|
|
|
@ -265,6 +265,8 @@ private:
|
|||
void botSharePhone(Fn<void(bool shared)> callback) override;
|
||||
void botInvokeCustomMethod(
|
||||
Ui::BotWebView::CustomMethodRequest request) override;
|
||||
void botSendPreparedMessage(
|
||||
Ui::BotWebView::SendPreparedMessageRequest request) override;
|
||||
void botSetEmojiStatus(
|
||||
Ui::BotWebView::SetEmojiStatusRequest request) override;
|
||||
void botOpenPrivacyPolicy() override;
|
||||
|
|
196
Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.cpp
Normal file
196
Telegram/SourceFiles/inline_bots/inline_bot_confirm_prepared.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "inline_bots/inline_bot_confirm_prepared.h"
|
||||
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/layers/generic_box.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"
|
||||
|
||||
namespace InlineBots {
|
||||
namespace {
|
||||
|
||||
using namespace HistoryView;
|
||||
|
||||
class PreviewDelegate final : public DefaultElementDelegate {
|
||||
public:
|
||||
PreviewDelegate(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Ui::ChatStyle*> st,
|
||||
Fn<void()> update);
|
||||
|
||||
bool elementAnimationsPaused() override;
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
Context elementContext() override;
|
||||
|
||||
private:
|
||||
const not_null<QWidget*> _parent;
|
||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||
|
||||
};
|
||||
|
||||
class PreviewWrap final : public Ui::RpWidget {
|
||||
public:
|
||||
PreviewWrap(not_null<QWidget*> parent, not_null<HistoryItem*> item);
|
||||
~PreviewWrap();
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void resizeTo(int width);
|
||||
void prepare(not_null<HistoryItem*> item);
|
||||
|
||||
const not_null<History*> _history;
|
||||
const std::unique_ptr<Ui::ChatTheme> _theme;
|
||||
const std::unique_ptr<Ui::ChatStyle> _style;
|
||||
const std::unique_ptr<PreviewDelegate> _delegate;
|
||||
AdminLog::OwnedItem _item;
|
||||
QPoint _position;
|
||||
|
||||
};
|
||||
|
||||
PreviewDelegate::PreviewDelegate(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Ui::ChatStyle*> st,
|
||||
Fn<void()> update)
|
||||
: _parent(parent)
|
||||
, _pathGradient(MakePathShiftGradient(st, update)) {
|
||||
}
|
||||
|
||||
bool PreviewDelegate::elementAnimationsPaused() {
|
||||
return _parent->window()->isActiveWindow();
|
||||
}
|
||||
|
||||
auto PreviewDelegate::elementPathShiftGradient()
|
||||
-> not_null<Ui::PathShiftGradient*> {
|
||||
return _pathGradient.get();
|
||||
}
|
||||
|
||||
Context PreviewDelegate::elementContext() {
|
||||
return Context::History;
|
||||
}
|
||||
|
||||
PreviewWrap::PreviewWrap(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<HistoryItem*> item)
|
||||
: RpWidget(parent)
|
||||
, _history(item->history())
|
||||
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
|
||||
, _style(std::make_unique<Ui::ChatStyle>(
|
||||
_history->session().colorIndicesValue()))
|
||||
, _delegate(std::make_unique<PreviewDelegate>(
|
||||
parent,
|
||||
_style.get(),
|
||||
[=] { update(); }))
|
||||
, _position(0, st::msgMargin.bottom()) {
|
||||
_style->apply(_theme.get());
|
||||
|
||||
using namespace HistoryView;
|
||||
_history->owner().viewRepaintRequest(
|
||||
) | rpl::start_with_next([=](not_null<const Element*> view) {
|
||||
if (view == _item.get()) {
|
||||
update();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
_history->session().downloaderTaskFinished() | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
prepare(item);
|
||||
}
|
||||
|
||||
PreviewWrap::~PreviewWrap() {
|
||||
_item = {};
|
||||
}
|
||||
|
||||
void PreviewWrap::prepare(not_null<HistoryItem*> item) {
|
||||
_item = AdminLog::OwnedItem(_delegate.get(), item);
|
||||
if (width() >= st::msgMinWidth) {
|
||||
resizeTo(width());
|
||||
}
|
||||
|
||||
widthValue(
|
||||
) | rpl::filter([=](int width) {
|
||||
return width >= st::msgMinWidth;
|
||||
}) | rpl::start_with_next([=](int width) {
|
||||
resizeTo(width);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void PreviewWrap::resizeTo(int width) {
|
||||
const auto height = _position.y()
|
||||
+ _item->resizeGetHeight(width)
|
||||
+ _position.y()
|
||||
+ st::msgServiceMargin.top()
|
||||
+ st::msgServiceGiftBoxTopSkip
|
||||
- st::msgServiceMargin.bottom();
|
||||
resize(width, height);
|
||||
}
|
||||
|
||||
void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||
auto p = Painter(this);
|
||||
|
||||
const auto clip = e->rect();
|
||||
if (!clip.isEmpty()) {
|
||||
p.setClipRect(clip);
|
||||
Window::SectionWidget::PaintBackground(
|
||||
p,
|
||||
_theme.get(),
|
||||
QSize(width(), window()->height()),
|
||||
clip);
|
||||
}
|
||||
|
||||
auto context = _theme->preparePaintContext(
|
||||
_style.get(),
|
||||
rect(),
|
||||
e->rect(),
|
||||
!window()->isActiveWindow());
|
||||
p.translate(_position);
|
||||
_item->draw(p, context);
|
||||
}
|
||||
|
||||
} // namesace
|
||||
|
||||
void PreparedPreviewBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<HistoryItem*> item,
|
||||
Fn<void()> share) {
|
||||
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(); });
|
||||
|
||||
item->history()->owner().itemRemoved(
|
||||
) | rpl::start_with_next([=](not_null<const HistoryItem*> removed) {
|
||||
if (removed == item) {
|
||||
box->closeBox();
|
||||
}
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
} // namespace InlineBots
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Ui {
|
||||
class GenericBox;
|
||||
} // namespace Ui
|
||||
|
||||
namespace InlineBots {
|
||||
|
||||
void PreparedPreviewBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<HistoryItem*> item,
|
||||
Fn<void()> share);
|
||||
|
||||
} // namespace InlineBots
|
|
@ -378,14 +378,20 @@ bool Result::hasThumbDisplay() const {
|
|||
void Result::addToHistory(
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const {
|
||||
fields.flags |= MessageFlag::FromInlineBot;
|
||||
history->addNewLocalMessage(makeMessage(history, std::move(fields)));
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> Result::makeMessage(
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const {
|
||||
fields.flags |= MessageFlag::FromInlineBot | MessageFlag::Local;
|
||||
if (_replyMarkup) {
|
||||
fields.markup = *_replyMarkup;
|
||||
if (!fields.markup.isNull()) {
|
||||
fields.flags |= MessageFlag::HasReplyMarkup;
|
||||
}
|
||||
}
|
||||
sendData->addToHistory(this, history, std::move(fields));
|
||||
return sendData->makeMessage(this, history, std::move(fields));
|
||||
}
|
||||
|
||||
QString Result::getErrorOnSend(not_null<History*> history) const {
|
||||
|
|
|
@ -66,6 +66,9 @@ public:
|
|||
void addToHistory(
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const;
|
||||
[[nodiscard]] not_null<HistoryItem*> makeMessage(
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const;
|
||||
QString getErrorOnSend(not_null<History*> history) const;
|
||||
|
||||
// interface for Layout:: usage
|
||||
|
|
|
@ -28,7 +28,7 @@ QString SendData::getLayoutDescription(const Result *owner) const {
|
|||
return owner->_description;
|
||||
}
|
||||
|
||||
void SendDataCommon::addToHistory(
|
||||
not_null<HistoryItem*> SendDataCommon::makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const {
|
||||
|
@ -36,7 +36,7 @@ void SendDataCommon::addToHistory(
|
|||
if (fields.replyTo) {
|
||||
fields.flags |= MessageFlag::HasReplyInfo;
|
||||
}
|
||||
history->addNewLocalMessage(
|
||||
return history->makeMessage(
|
||||
std::move(fields),
|
||||
std::move(distinct.text),
|
||||
std::move(distinct.media));
|
||||
|
@ -96,14 +96,14 @@ QString SendContact::getLayoutDescription(const Result *owner) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
void SendPhoto::addToHistory(
|
||||
not_null<HistoryItem*> SendPhoto::makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const {
|
||||
history->addNewLocalMessage(
|
||||
return history->makeMessage(
|
||||
std::move(fields),
|
||||
_photo,
|
||||
{ _message, _entities });
|
||||
TextWithEntities{ _message, _entities });
|
||||
}
|
||||
|
||||
QString SendPhoto::getErrorOnSend(
|
||||
|
@ -113,14 +113,14 @@ QString SendPhoto::getErrorOnSend(
|
|||
return Data::RestrictionError(history->peer, type).value_or(QString());
|
||||
}
|
||||
|
||||
void SendFile::addToHistory(
|
||||
not_null<HistoryItem*> SendFile::makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const {
|
||||
history->addNewLocalMessage(
|
||||
return history->makeMessage(
|
||||
std::move(fields),
|
||||
_document,
|
||||
{ _message, _entities });
|
||||
TextWithEntities{ _message, _entities });
|
||||
}
|
||||
|
||||
QString SendFile::getErrorOnSend(
|
||||
|
@ -130,11 +130,11 @@ QString SendFile::getErrorOnSend(
|
|||
return Data::RestrictionError(history->peer, type).value_or(QString());
|
||||
}
|
||||
|
||||
void SendGame::addToHistory(
|
||||
not_null<HistoryItem*> SendGame::makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const {
|
||||
history->addNewLocalMessage(std::move(fields), _game);
|
||||
return history->addNewLocalMessage(std::move(fields), _game);
|
||||
}
|
||||
|
||||
QString SendGame::getErrorOnSend(
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
virtual void addToHistory(
|
||||
virtual not_null<HistoryItem*> makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const = 0;
|
||||
|
@ -75,7 +75,7 @@ public:
|
|||
};
|
||||
virtual SentMessageFields getSentMessageFields() const = 0;
|
||||
|
||||
void addToHistory(
|
||||
not_null<HistoryItem*> makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const override;
|
||||
|
@ -236,7 +236,7 @@ public:
|
|||
return _photo != nullptr;
|
||||
}
|
||||
|
||||
void addToHistory(
|
||||
not_null<HistoryItem*> makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const override;
|
||||
|
@ -270,7 +270,7 @@ public:
|
|||
return _document != nullptr;
|
||||
}
|
||||
|
||||
void addToHistory(
|
||||
not_null<HistoryItem*> makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const override;
|
||||
|
@ -298,7 +298,7 @@ public:
|
|||
return _game != nullptr;
|
||||
}
|
||||
|
||||
void addToHistory(
|
||||
not_null<HistoryItem*> makeMessage(
|
||||
const Result *owner,
|
||||
not_null<History*> history,
|
||||
HistoryItemCommonFields &&fields) const override;
|
||||
|
|
|
@ -183,7 +183,8 @@ struct Panel::WebviewWithLifetime {
|
|||
Webview::WindowConfig config = Webview::WindowConfig());
|
||||
|
||||
Webview::Window window;
|
||||
QPointer<RpWidget> lastHidingBox;
|
||||
std::vector<QPointer<RpWidget>> boxes;
|
||||
rpl::lifetime boxesLifetime;
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
|
||||
|
@ -824,6 +825,8 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) {
|
|||
processHeaderColor(arguments);
|
||||
} else if (command == "web_app_set_bottom_bar_color") {
|
||||
processBottomBarColor(arguments);
|
||||
} else if (command == "web_app_send_prepared_message") {
|
||||
processSendMessageRequest(arguments);
|
||||
} else if (command == "web_app_set_emoji_status") {
|
||||
processEmojiStatusRequest(arguments);
|
||||
} else if (command == "web_app_request_emoji_status_access") {
|
||||
|
@ -967,6 +970,27 @@ void Panel::switchInlineQueryMessage(const QJsonObject &args) {
|
|||
_delegate->botSwitchInlineQuery(types, query);
|
||||
}
|
||||
|
||||
void Panel::processSendMessageRequest(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_delegate->botClose();
|
||||
return;
|
||||
}
|
||||
const auto id = args["id"].toString();
|
||||
auto callback = crl::guard(this, [=](QString error) {
|
||||
if (error.isEmpty()) {
|
||||
postEvent("prepared_message_sent");
|
||||
} else {
|
||||
postEvent(
|
||||
"prepared_message_failed",
|
||||
u"{ error: \"%1\" }"_q.arg(error));
|
||||
}
|
||||
});
|
||||
_delegate->botSendPreparedMessage({
|
||||
.id = id,
|
||||
.callback = std::move(callback),
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::processEmojiStatusRequest(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
_delegate->botClose();
|
||||
|
@ -1502,9 +1526,11 @@ void Panel::layoutButtons() {
|
|||
return button && !button->isHidden();
|
||||
};
|
||||
const auto any = shown(_mainButton) || shown(_secondaryButton);
|
||||
_webviewBottom->setVisible(!any && !_fullscreen.current());
|
||||
_webviewBottom->setVisible(!any
|
||||
&& !_fullscreen.current()
|
||||
&& !_layerShown);
|
||||
if (any) {
|
||||
_bottomButtonsBg->show();
|
||||
_bottomButtonsBg->setVisible(!_layerShown);
|
||||
|
||||
const auto one = shown(_mainButton)
|
||||
? _mainButton.get()
|
||||
|
@ -1570,7 +1596,9 @@ void Panel::layoutButtons() {
|
|||
} else if (_bottomButtonsBg) {
|
||||
_bottomButtonsBg->hide();
|
||||
}
|
||||
_footerHeight = any
|
||||
_footerHeight = _layerShown
|
||||
? 0
|
||||
: any
|
||||
? _bottomButtonsBg->height()
|
||||
: _fullscreen.current()
|
||||
? 0
|
||||
|
@ -1586,25 +1614,34 @@ void Panel::showBox(
|
|||
LayerOptions options,
|
||||
anim::type animated) {
|
||||
if (const auto widget = _webview ? _webview->window.widget() : nullptr) {
|
||||
_layerShown = true;
|
||||
const auto hideNow = !widget->isHidden();
|
||||
if (hideNow || _webview->lastHidingBox) {
|
||||
const auto raw = _webview->lastHidingBox = box.data();
|
||||
box->boxClosing(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto raw = box.data();
|
||||
_webview->boxes.push_back(raw);
|
||||
raw->boxClosing(
|
||||
) | rpl::filter([=] {
|
||||
return _webview != nullptr;
|
||||
}) | rpl::start_with_next([=] {
|
||||
auto &list = _webview->boxes;
|
||||
list.erase(ranges::remove_if(list, [&](QPointer<RpWidget> b) {
|
||||
return !b || (b == raw);
|
||||
}), end(list));
|
||||
if (list.empty()) {
|
||||
_webview->boxesLifetime.destroy();
|
||||
_layerShown = false;
|
||||
const auto widget = _webview
|
||||
? _webview->window.widget()
|
||||
: nullptr;
|
||||
if (widget
|
||||
&& widget->isHidden()
|
||||
&& _webview->lastHidingBox == raw) {
|
||||
if (widget && widget->isHidden()) {
|
||||
widget->show();
|
||||
_webviewBottom->setVisible(!_fullscreen.current());
|
||||
layoutButtons();
|
||||
}
|
||||
}, _webview->lifetime);
|
||||
if (hideNow) {
|
||||
widget->hide();
|
||||
_webviewBottom->hide();
|
||||
}
|
||||
}, _webview->boxesLifetime);
|
||||
|
||||
if (hideNow) {
|
||||
widget->hide();
|
||||
layoutButtons();
|
||||
}
|
||||
}
|
||||
const auto raw = box.data();
|
||||
|
|
|
@ -57,6 +57,11 @@ struct SetEmojiStatusRequest {
|
|||
Fn<void(QString)> callback;
|
||||
};
|
||||
|
||||
struct SendPreparedMessageRequest {
|
||||
QString id = 0;
|
||||
Fn<void(QString)> callback;
|
||||
};
|
||||
|
||||
class Delegate {
|
||||
public:
|
||||
virtual Webview::ThemeParams botThemeParams() = 0;
|
||||
|
@ -76,6 +81,8 @@ public:
|
|||
virtual void botSharePhone(Fn<void(bool shared)> callback) = 0;
|
||||
virtual void botInvokeCustomMethod(CustomMethodRequest request) = 0;
|
||||
virtual void botSetEmojiStatus(SetEmojiStatusRequest request) = 0;
|
||||
virtual void botSendPreparedMessage(
|
||||
SendPreparedMessageRequest request) = 0;
|
||||
virtual void botOpenPrivacyPolicy() = 0;
|
||||
virtual void botClose() = 0;
|
||||
};
|
||||
|
@ -132,6 +139,7 @@ private:
|
|||
void setTitle(rpl::producer<QString> title);
|
||||
void sendDataMessage(const QJsonObject &args);
|
||||
void switchInlineQueryMessage(const QJsonObject &args);
|
||||
void processSendMessageRequest(const QJsonObject &args);
|
||||
void processEmojiStatusRequest(const QJsonObject &args);
|
||||
void processEmojiStatusAccessRequest();
|
||||
void processButtonMessage(
|
||||
|
@ -195,6 +203,7 @@ private:
|
|||
rpl::lifetime _headerColorLifetime;
|
||||
rpl::lifetime _bottomBarColorLifetime;
|
||||
rpl::variable<bool> _fullscreen = false;
|
||||
bool _layerShown = false;
|
||||
bool _webviewProgress = false;
|
||||
bool _themeUpdateScheduled = false;
|
||||
bool _hiddenForPayment = false;
|
||||
|
|
|
@ -1881,8 +1881,8 @@ void BlockSenderFromRepliesBox(
|
|||
Window::ClearReply{ id });
|
||||
}
|
||||
|
||||
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
||||
not_null<Main::Session*> session,
|
||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||
rpl::producer<QString> titleOverride,
|
||||
FnMut<void()> &&successCallback,
|
||||
|
@ -1927,15 +1927,30 @@ QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
|||
box->setTitle(std::move(titleOverride));
|
||||
}
|
||||
};
|
||||
*weak = navigation->parentController()->show(Box<PeerListBox>(
|
||||
auto result = Box<PeerListBox>(
|
||||
std::make_unique<ChooseRecipientBoxController>(ChooseRecipientArgs{
|
||||
.session = &navigation->session(),
|
||||
.session = session,
|
||||
.callback = std::move(callback),
|
||||
.filter = std::move(filter),
|
||||
.premiumRequiredError = WritePremiumRequiredError,
|
||||
}),
|
||||
std::move(initBox)));
|
||||
return weak->data();
|
||||
std::move(initBox));
|
||||
*weak = result.data();
|
||||
return result;
|
||||
}
|
||||
|
||||
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||
rpl::producer<QString> titleOverride,
|
||||
FnMut<void()> &&successCallback,
|
||||
InlineBots::PeerTypes typesRestriction) {
|
||||
return navigation->parentController()->show(PrepareChooseRecipientBox(
|
||||
&navigation->session(),
|
||||
std::move(chosen),
|
||||
std::move(titleOverride),
|
||||
std::move(successCallback),
|
||||
typesRestriction));
|
||||
}
|
||||
|
||||
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "api/api_common.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "menu/menu_send.h"
|
||||
#include "data/data_poll.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||
|
@ -138,6 +139,12 @@ Fn<void()> DeleteAndLeaveHandler(
|
|||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
|
||||
not_null<Main::Session*> session,
|
||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||
rpl::producer<QString> titleOverride = nullptr,
|
||||
FnMut<void()> &&successCallback = nullptr,
|
||||
InlineBots::PeerTypes typesRestriction = 0);
|
||||
QPointer<Ui::BoxContent> ShowChooseRecipientBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
FnMut<bool(not_null<Data::Thread*>)> &&chosen,
|
||||
|
|
Loading…
Add table
Reference in a new issue