mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support business bot state in chat.
This commit is contained in:
parent
3d97ea6f96
commit
cf1d0677d1
20 changed files with 588 additions and 52 deletions
|
@ -2288,6 +2288,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_chatbots_not_found" = "Chatbot not found.";
|
"lng_chatbots_not_found" = "Chatbot not found.";
|
||||||
"lng_chatbots_add" = "Add";
|
"lng_chatbots_add" = "Add";
|
||||||
"lng_chatbots_info_url" = "https://telegram.org/privacy";
|
"lng_chatbots_info_url" = "https://telegram.org/privacy";
|
||||||
|
"lng_chatbot_status_can_reply" = "bot manages this chat";
|
||||||
|
"lng_chatbot_status_paused" = "bot stopped";
|
||||||
|
"lng_chatbot_status_views" = "bot has access to this chat";
|
||||||
|
"lng_chatbot_button_pause" = "Stop";
|
||||||
|
"lng_chatbot_button_resume" = "Start";
|
||||||
|
"lng_chatbot_menu_manage" = "Manage bot";
|
||||||
|
"lng_chatbot_menu_remove" = "Remove bot from this chat";
|
||||||
|
"lng_chatbot_menu_revoke" = "Revoke access to this chat";
|
||||||
|
|
||||||
"lng_boost_channel_button" = "Boost Channel";
|
"lng_boost_channel_button" = "Boost Channel";
|
||||||
"lng_boost_group_button" = "Boost Group";
|
"lng_boost_group_button" = "Boost Group";
|
||||||
|
|
|
@ -351,9 +351,9 @@ Main::Session &EditFilterChatsListController::session() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int EditFilterChatsListController::selectedTypesCount() const {
|
int EditFilterChatsListController::selectedTypesCount() const {
|
||||||
Expects(_chatlist || _typesDelegate != nullptr);
|
Expects(_chatlist || !_options || _typesDelegate != nullptr);
|
||||||
|
|
||||||
if (_chatlist) {
|
if (_chatlist || !_options) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
|
@ -396,7 +396,7 @@ bool EditFilterChatsListController::handleDeselectForeignRow(
|
||||||
|
|
||||||
void EditFilterChatsListController::prepareViewHook() {
|
void EditFilterChatsListController::prepareViewHook() {
|
||||||
delegate()->peerListSetTitle(std::move(_title));
|
delegate()->peerListSetTitle(std::move(_title));
|
||||||
if (!_chatlist) {
|
if (!_chatlist && _options) {
|
||||||
delegate()->peerListSetAboveWidget(prepareTypesList());
|
delegate()->peerListSetAboveWidget(prepareTypesList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,7 +479,8 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() {
|
||||||
|
|
||||||
auto EditFilterChatsListController::createRow(not_null<History*> history)
|
auto EditFilterChatsListController::createRow(not_null<History*> history)
|
||||||
-> std::unique_ptr<Row> {
|
-> std::unique_ptr<Row> {
|
||||||
const auto business = _options & (Flag::NewChats | Flag::ExistingChats);
|
const auto business = (_options & (Flag::NewChats | Flag::ExistingChats))
|
||||||
|
|| (!_options && !_chatlist);
|
||||||
if (business && (history->peer->isSelf() || !history->peer->isUser())) {
|
if (business && (history->peer->isSelf() || !history->peer->isUser())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -857,6 +857,31 @@ historyEmojiStatusInfoLabel: FlatLabel(historyContactStatusLabel) {
|
||||||
}
|
}
|
||||||
historyContactStatusMinSkip: 16px;
|
historyContactStatusMinSkip: 16px;
|
||||||
|
|
||||||
|
historyBusinessBotPhoto: UserpicButton(defaultUserpicButton) {
|
||||||
|
size: size(46px, 46px);
|
||||||
|
photoSize: 46px;
|
||||||
|
photoPosition: point(0px, 0px);
|
||||||
|
}
|
||||||
|
historyBusinessBotName: FlatLabel(defaultFlatLabel) {
|
||||||
|
style: semiboldTextStyle;
|
||||||
|
}
|
||||||
|
historyBusinessBotStatus: FlatLabel(historyContactStatusLabel) {
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
}
|
||||||
|
historyBusinessBotToggle: defaultActiveButton;
|
||||||
|
historyBusinessBotSettings: IconButton(defaultIconButton) {
|
||||||
|
icon: icon{{ "menu/customize", menuIconFg }};
|
||||||
|
iconOver: icon{{ "menu/customize", menuIconFgOver }};
|
||||||
|
iconPosition: point(-1px, -1px);
|
||||||
|
rippleAreaSize: 40px;
|
||||||
|
rippleAreaPosition: point(4px, 9px);
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: windowBgOver;
|
||||||
|
}
|
||||||
|
height: 58px;
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
historyReplyCancelIcon: icon {{ "box_button_close", historyReplyCancelFg }};
|
historyReplyCancelIcon: icon {{ "box_button_close", historyReplyCancelFg }};
|
||||||
historyReplyCancelIconOver: icon {{ "box_button_close", historyReplyCancelFgOver }};
|
historyReplyCancelIconOver: icon {{ "box_button_close", historyReplyCancelFgOver }};
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ void Chatbots::save(
|
||||||
? Flag::f_can_reply
|
? Flag::f_can_reply
|
||||||
: Flag()),
|
: Flag()),
|
||||||
(settings.bot ? settings.bot : was.bot)->inputUser,
|
(settings.bot ? settings.bot : was.bot)->inputUser,
|
||||||
ToMTP(settings.recipients)
|
ForBotsToMTP(settings.recipients)
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
api->applyUpdates(result);
|
api->applyUpdates(result);
|
||||||
if (done) {
|
if (done) {
|
||||||
|
@ -103,4 +103,72 @@ void Chatbots::save(
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Chatbots::togglePaused(not_null<PeerData*> peer, bool paused) {
|
||||||
|
const auto type = paused
|
||||||
|
? SentRequestType::Pause
|
||||||
|
: SentRequestType::Unpause;
|
||||||
|
const auto api = &_owner->session().api();
|
||||||
|
const auto i = _sentRequests.find(peer);
|
||||||
|
if (i != end(_sentRequests)) {
|
||||||
|
const auto already = i->second.type;
|
||||||
|
if (already == SentRequestType::Remove || already == type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api->request(i->second.requestId).cancel();
|
||||||
|
_sentRequests.erase(i);
|
||||||
|
}
|
||||||
|
const auto id = api->request(MTPaccount_ToggleConnectedBotPaused(
|
||||||
|
peer->input,
|
||||||
|
MTP_bool(paused)
|
||||||
|
)).done([=] {
|
||||||
|
if (_sentRequests[peer].type != type) {
|
||||||
|
return;
|
||||||
|
} else if (const auto settings = peer->barSettings()) {
|
||||||
|
peer->setBarSettings(paused
|
||||||
|
? (*settings | PeerBarSetting::BusinessBotPaused)
|
||||||
|
: (*settings & ~PeerBarSetting::BusinessBotPaused));
|
||||||
|
} else {
|
||||||
|
api->requestPeerSettings(peer);
|
||||||
|
}
|
||||||
|
_sentRequests.remove(peer);
|
||||||
|
}).fail([=] {
|
||||||
|
if (_sentRequests[peer].type != type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api->requestPeerSettings(peer);
|
||||||
|
_sentRequests.remove(peer);
|
||||||
|
}).send();
|
||||||
|
_sentRequests[peer] = SentRequest{ type, id };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chatbots::removeFrom(not_null<PeerData*> peer) {
|
||||||
|
const auto type = SentRequestType::Remove;
|
||||||
|
const auto api = &_owner->session().api();
|
||||||
|
const auto i = _sentRequests.find(peer);
|
||||||
|
if (i != end(_sentRequests)) {
|
||||||
|
const auto already = i->second.type;
|
||||||
|
if (already == type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api->request(i->second.requestId).cancel();
|
||||||
|
_sentRequests.erase(i);
|
||||||
|
}
|
||||||
|
const auto id = api->request(MTPaccount_DisablePeerConnectedBot(
|
||||||
|
peer->input
|
||||||
|
)).done([=] {
|
||||||
|
if (_sentRequests[peer].type != type) {
|
||||||
|
return;
|
||||||
|
} else if (const auto settings = peer->barSettings()) {
|
||||||
|
peer->clearBusinessBot();
|
||||||
|
} else {
|
||||||
|
api->requestPeerSettings(peer);
|
||||||
|
}
|
||||||
|
_sentRequests.remove(peer);
|
||||||
|
}).fail([=] {
|
||||||
|
api->requestPeerSettings(peer);
|
||||||
|
_sentRequests.remove(peer);
|
||||||
|
}).send();
|
||||||
|
_sentRequests[peer] = SentRequest{ type, id };
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -41,13 +41,28 @@ public:
|
||||||
Fn<void()> done,
|
Fn<void()> done,
|
||||||
Fn<void(QString)> fail);
|
Fn<void(QString)> fail);
|
||||||
|
|
||||||
|
void togglePaused(not_null<PeerData*> peer, bool paused);
|
||||||
|
void removeFrom(not_null<PeerData*> peer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class SentRequestType {
|
||||||
|
Pause,
|
||||||
|
Unpause,
|
||||||
|
Remove,
|
||||||
|
};
|
||||||
|
struct SentRequest {
|
||||||
|
SentRequestType type = SentRequestType::Pause;
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
|
|
||||||
rpl::variable<ChatbotsSettings> _settings;
|
rpl::variable<ChatbotsSettings> _settings;
|
||||||
mtpRequestId _requestId = 0;
|
mtpRequestId _requestId = 0;
|
||||||
bool _loaded = false;
|
bool _loaded = false;
|
||||||
|
|
||||||
|
base::flat_map<not_null<PeerData*>, SentRequest> _sentRequests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -51,16 +51,13 @@ constexpr auto kInNextDayMax = WorkingInterval::kInNextDayMax;
|
||||||
return intervals;
|
return intervals;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
template <typename Flag>
|
||||||
|
auto RecipientsFlags(const BusinessRecipients &data) {
|
||||||
MTPInputBusinessRecipients ToMTP(
|
|
||||||
const BusinessRecipients &data) {
|
|
||||||
using Flag = MTPDinputBusinessRecipients::Flag;
|
|
||||||
using Type = BusinessChatType;
|
|
||||||
const auto &chats = data.allButExcluded
|
const auto &chats = data.allButExcluded
|
||||||
? data.excluded
|
? data.excluded
|
||||||
: data.included;
|
: data.included;
|
||||||
const auto flags = Flag()
|
using Type = BusinessChatType;
|
||||||
|
return Flag()
|
||||||
| ((chats.types & Type::NewChats) ? Flag::f_new_chats : Flag())
|
| ((chats.types & Type::NewChats) ? Flag::f_new_chats : Flag())
|
||||||
| ((chats.types & Type::ExistingChats)
|
| ((chats.types & Type::ExistingChats)
|
||||||
? Flag::f_existing_chats
|
? Flag::f_existing_chats
|
||||||
|
@ -69,12 +66,30 @@ MTPInputBusinessRecipients ToMTP(
|
||||||
| ((chats.types & Type::NonContacts) ? Flag::f_non_contacts : Flag())
|
| ((chats.types & Type::NonContacts) ? Flag::f_non_contacts : Flag())
|
||||||
| (chats.list.empty() ? Flag() : Flag::f_users)
|
| (chats.list.empty() ? Flag() : Flag::f_users)
|
||||||
| (data.allButExcluded ? Flag::f_exclude_selected : Flag());
|
| (data.allButExcluded ? Flag::f_exclude_selected : Flag());
|
||||||
const auto &users = data.allButExcluded
|
}
|
||||||
? data.excluded
|
|
||||||
: data.included;
|
} // namespace
|
||||||
|
|
||||||
|
MTPInputBusinessRecipients ForMessagesToMTP(const BusinessRecipients &data) {
|
||||||
|
using Flag = MTPDinputBusinessRecipients::Flag;
|
||||||
|
const auto &chats = data.allButExcluded ? data.excluded : data.included;
|
||||||
return MTP_inputBusinessRecipients(
|
return MTP_inputBusinessRecipients(
|
||||||
MTP_flags(flags),
|
MTP_flags(RecipientsFlags<Flag>(data)),
|
||||||
MTP_vector_from_range(users.list
|
MTP_vector_from_range(chats.list
|
||||||
|
| ranges::views::transform(&UserData::inputUser)));
|
||||||
|
}
|
||||||
|
|
||||||
|
MTPInputBusinessBotRecipients ForBotsToMTP(const BusinessRecipients &data) {
|
||||||
|
using Flag = MTPDinputBusinessBotRecipients::Flag;
|
||||||
|
const auto &chats = data.allButExcluded ? data.excluded : data.included;
|
||||||
|
return MTP_inputBusinessBotRecipients(
|
||||||
|
MTP_flags(RecipientsFlags<Flag>(data)
|
||||||
|
| ((data.allButExcluded || data.excluded.empty())
|
||||||
|
? Flag()
|
||||||
|
: Flag::f_exclude_users)),
|
||||||
|
MTP_vector_from_range(chats.list
|
||||||
|
| ranges::views::transform(&UserData::inputUser)),
|
||||||
|
MTP_vector_from_range(data.excluded.list
|
||||||
| ranges::views::transform(&UserData::inputUser)));
|
| ranges::views::transform(&UserData::inputUser)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +118,40 @@ BusinessRecipients FromMTP(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] BusinessDetails FromMTP(
|
BusinessRecipients FromMTP(
|
||||||
|
not_null<Session*> owner,
|
||||||
|
const MTPBusinessBotRecipients &recipients) {
|
||||||
|
using Type = BusinessChatType;
|
||||||
|
|
||||||
|
const auto &data = recipients.data();
|
||||||
|
auto result = BusinessRecipients{
|
||||||
|
.allButExcluded = data.is_exclude_selected(),
|
||||||
|
};
|
||||||
|
auto &chats = result.allButExcluded
|
||||||
|
? result.excluded
|
||||||
|
: result.included;
|
||||||
|
chats.types = Type()
|
||||||
|
| (data.is_new_chats() ? Type::NewChats : Type())
|
||||||
|
| (data.is_existing_chats() ? Type::ExistingChats : Type())
|
||||||
|
| (data.is_contacts() ? Type::Contacts : Type())
|
||||||
|
| (data.is_non_contacts() ? Type::NonContacts : Type());
|
||||||
|
if (const auto users = data.vusers()) {
|
||||||
|
for (const auto &userId : users->v) {
|
||||||
|
chats.list.push_back(owner->user(UserId(userId.v)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!result.allButExcluded) {
|
||||||
|
if (const auto excluded = data.vexclude_users()) {
|
||||||
|
for (const auto &userId : excluded->v) {
|
||||||
|
result.excluded.list.push_back(
|
||||||
|
owner->user(UserId(userId.v)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BusinessDetails FromMTP(
|
||||||
const tl::conditional<MTPBusinessWorkHours> &hours,
|
const tl::conditional<MTPBusinessWorkHours> &hours,
|
||||||
const tl::conditional<MTPBusinessLocation> &location) {
|
const tl::conditional<MTPBusinessLocation> &location) {
|
||||||
auto result = BusinessDetails();
|
auto result = BusinessDetails();
|
||||||
|
|
|
@ -49,11 +49,21 @@ struct BusinessRecipients {
|
||||||
const BusinessRecipients &b) = default;
|
const BusinessRecipients &b) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] MTPInputBusinessRecipients ToMTP(
|
enum class BusinessRecipientsType : uchar {
|
||||||
|
Messages,
|
||||||
|
Bots,
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] MTPInputBusinessRecipients ForMessagesToMTP(
|
||||||
|
const BusinessRecipients &data);
|
||||||
|
[[nodiscard]] MTPInputBusinessBotRecipients ForBotsToMTP(
|
||||||
const BusinessRecipients &data);
|
const BusinessRecipients &data);
|
||||||
[[nodiscard]] BusinessRecipients FromMTP(
|
[[nodiscard]] BusinessRecipients FromMTP(
|
||||||
not_null<Session*> owner,
|
not_null<Session*> owner,
|
||||||
const MTPBusinessRecipients &recipients);
|
const MTPBusinessRecipients &recipients);
|
||||||
|
[[nodiscard]] BusinessRecipients FromMTP(
|
||||||
|
not_null<Session*> owner,
|
||||||
|
const MTPBusinessBotRecipients &recipients);
|
||||||
|
|
||||||
struct Timezone {
|
struct Timezone {
|
||||||
QString id;
|
QString id;
|
||||||
|
|
|
@ -49,14 +49,14 @@ namespace {
|
||||||
MTP_flags(data.offlineOnly ? Flag::f_offline_only : Flag()),
|
MTP_flags(data.offlineOnly ? Flag::f_offline_only : Flag()),
|
||||||
MTP_int(data.shortcutId),
|
MTP_int(data.shortcutId),
|
||||||
ToMTP(data.schedule),
|
ToMTP(data.schedule),
|
||||||
ToMTP(data.recipients));
|
ForMessagesToMTP(data.recipients));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] MTPInputBusinessGreetingMessage ToMTP(
|
[[nodiscard]] MTPInputBusinessGreetingMessage ToMTP(
|
||||||
const GreetingSettings &data) {
|
const GreetingSettings &data) {
|
||||||
return MTP_inputBusinessGreetingMessage(
|
return MTP_inputBusinessGreetingMessage(
|
||||||
MTP_int(data.shortcutId),
|
MTP_int(data.shortcutId),
|
||||||
ToMTP(data.recipients),
|
ForMessagesToMTP(data.recipients),
|
||||||
MTP_int(data.noActivityDays));
|
MTP_int(data.noActivityDays));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -608,6 +608,23 @@ void PeerData::checkFolder(FolderId folderId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PeerData::clearBusinessBot() {
|
||||||
|
if (const auto details = _barDetails.get()) {
|
||||||
|
if (details->requestChatDate) {
|
||||||
|
details->businessBot = nullptr;
|
||||||
|
details->businessBotManageUrl = QString();
|
||||||
|
} else {
|
||||||
|
_barDetails = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto settings = barSettings()) {
|
||||||
|
setBarSettings(*settings
|
||||||
|
& ~PeerBarSetting::BusinessBotPaused
|
||||||
|
& ~PeerBarSetting::BusinessBotCanReply
|
||||||
|
& ~PeerBarSetting::HasBusinessBot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PeerData::setTranslationDisabled(bool disabled) {
|
void PeerData::setTranslationDisabled(bool disabled) {
|
||||||
const auto flag = disabled
|
const auto flag = disabled
|
||||||
? TranslationFlag::Disabled
|
? TranslationFlag::Disabled
|
||||||
|
|
|
@ -373,6 +373,7 @@ public:
|
||||||
[[nodiscard]] TimeId requestChatDate() const;
|
[[nodiscard]] TimeId requestChatDate() const;
|
||||||
[[nodiscard]] UserData *businessBot() const;
|
[[nodiscard]] UserData *businessBot() const;
|
||||||
[[nodiscard]] QString businessBotManageUrl() const;
|
[[nodiscard]] QString businessBotManageUrl() const;
|
||||||
|
void clearBusinessBot();
|
||||||
|
|
||||||
enum class TranslationFlag : uchar {
|
enum class TranslationFlag : uchar {
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|
|
@ -1578,6 +1578,9 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
|
||||||
void HistoryWidget::orderWidgets() {
|
void HistoryWidget::orderWidgets() {
|
||||||
_voiceRecordBar->raise();
|
_voiceRecordBar->raise();
|
||||||
_send->raise();
|
_send->raise();
|
||||||
|
if (_businessBotStatus) {
|
||||||
|
_businessBotStatus->bar().raise();
|
||||||
|
}
|
||||||
if (_contactStatus) {
|
if (_contactStatus) {
|
||||||
_contactStatus->bar().raise();
|
_contactStatus->bar().raise();
|
||||||
}
|
}
|
||||||
|
@ -2282,17 +2285,30 @@ void HistoryWidget::showHistory(
|
||||||
_showAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
|
_showAtMsgHighlightPartOffsetHint = highlightPartOffsetHint;
|
||||||
_historyInited = false;
|
_historyInited = false;
|
||||||
_contactStatus = nullptr;
|
_contactStatus = nullptr;
|
||||||
|
_businessBotStatus = nullptr;
|
||||||
|
|
||||||
if (peerId) {
|
if (peerId) {
|
||||||
|
using namespace HistoryView;
|
||||||
_peer = session().data().peer(peerId);
|
_peer = session().data().peer(peerId);
|
||||||
_contactStatus = std::make_unique<HistoryView::ContactStatus>(
|
_contactStatus = std::make_unique<ContactStatus>(
|
||||||
controller(),
|
controller(),
|
||||||
this,
|
this,
|
||||||
_peer,
|
_peer,
|
||||||
false);
|
false);
|
||||||
_contactStatus->bar().heightValue() | rpl::start_with_next([=] {
|
_contactStatus->bar().heightValue(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}, _contactStatus->bar().lifetime());
|
}, _contactStatus->bar().lifetime());
|
||||||
|
if (const auto user = _peer->asUser()) {
|
||||||
|
_businessBotStatus = std::make_unique<BusinessBotStatus>(
|
||||||
|
controller(),
|
||||||
|
this,
|
||||||
|
user);
|
||||||
|
_businessBotStatus->bar().heightValue(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
updateControlsGeometry();
|
||||||
|
}, _businessBotStatus->bar().lifetime());
|
||||||
|
}
|
||||||
orderWidgets();
|
orderWidgets();
|
||||||
controller()->tabbedSelector()->setCurrentPeer(_peer);
|
controller()->tabbedSelector()->setCurrentPeer(_peer);
|
||||||
}
|
}
|
||||||
|
@ -2879,6 +2895,9 @@ void HistoryWidget::updateControlsVisibility() {
|
||||||
if (_contactStatus) {
|
if (_contactStatus) {
|
||||||
_contactStatus->show();
|
_contactStatus->show();
|
||||||
}
|
}
|
||||||
|
if (_businessBotStatus) {
|
||||||
|
_businessBotStatus->show();
|
||||||
|
}
|
||||||
if (isChoosingTheme()
|
if (isChoosingTheme()
|
||||||
|| (!editingMessage()
|
|| (!editingMessage()
|
||||||
&& (isSearching()
|
&& (isSearching()
|
||||||
|
@ -4059,6 +4078,9 @@ void HistoryWidget::hideChildWidgets() {
|
||||||
if (_contactStatus) {
|
if (_contactStatus) {
|
||||||
_contactStatus->hide();
|
_contactStatus->hide();
|
||||||
}
|
}
|
||||||
|
if (_businessBotStatus) {
|
||||||
|
_businessBotStatus->hide();
|
||||||
|
}
|
||||||
hideChildren();
|
hideChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5840,7 +5862,11 @@ void HistoryWidget::updateControlsGeometry() {
|
||||||
if (_contactStatus) {
|
if (_contactStatus) {
|
||||||
_contactStatus->bar().move(0, contactStatusTop);
|
_contactStatus->bar().move(0, contactStatusTop);
|
||||||
}
|
}
|
||||||
const auto scrollAreaTop = contactStatusTop + (_contactStatus ? _contactStatus->bar().height() : 0);
|
const auto businessBotTop = contactStatusTop + (_contactStatus ? _contactStatus->bar().height() : 0);
|
||||||
|
if (_businessBotStatus) {
|
||||||
|
_businessBotStatus->bar().move(0, businessBotTop);
|
||||||
|
}
|
||||||
|
const auto scrollAreaTop = businessBotTop + (_businessBotStatus ? _businessBotStatus->bar().height() : 0);
|
||||||
if (_scroll->y() != scrollAreaTop) {
|
if (_scroll->y() != scrollAreaTop) {
|
||||||
_scroll->moveToLeft(0, scrollAreaTop);
|
_scroll->moveToLeft(0, scrollAreaTop);
|
||||||
_fieldAutocomplete->setBoundings(_scroll->geometry());
|
_fieldAutocomplete->setBoundings(_scroll->geometry());
|
||||||
|
@ -6076,6 +6102,9 @@ void HistoryWidget::updateHistoryGeometry(
|
||||||
if (_contactStatus) {
|
if (_contactStatus) {
|
||||||
newScrollHeight -= _contactStatus->bar().height();
|
newScrollHeight -= _contactStatus->bar().height();
|
||||||
}
|
}
|
||||||
|
if (_businessBotStatus) {
|
||||||
|
newScrollHeight -= _businessBotStatus->bar().height();
|
||||||
|
}
|
||||||
if (isChoosingTheme()) {
|
if (isChoosingTheme()) {
|
||||||
newScrollHeight -= _chooseTheme->height();
|
newScrollHeight -= _chooseTheme->height();
|
||||||
} else if (!editingMessage()
|
} else if (!editingMessage()
|
||||||
|
@ -6457,6 +6486,7 @@ int HistoryWidget::computeMaxFieldHeight() const {
|
||||||
const auto available = height()
|
const auto available = height()
|
||||||
- _topBar->height()
|
- _topBar->height()
|
||||||
- (_contactStatus ? _contactStatus->bar().height() : 0)
|
- (_contactStatus ? _contactStatus->bar().height() : 0)
|
||||||
|
- (_businessBotStatus ? _businessBotStatus->bar().height() : 0)
|
||||||
- (_pinnedBar ? _pinnedBar->height() : 0)
|
- (_pinnedBar ? _pinnedBar->height() : 0)
|
||||||
- (_groupCallBar ? _groupCallBar->height() : 0)
|
- (_groupCallBar ? _groupCallBar->height() : 0)
|
||||||
- (_requestsBar ? _requestsBar->height() : 0)
|
- (_requestsBar ? _requestsBar->height() : 0)
|
||||||
|
|
|
@ -89,6 +89,7 @@ namespace HistoryView {
|
||||||
class StickerToast;
|
class StickerToast;
|
||||||
class TopBarWidget;
|
class TopBarWidget;
|
||||||
class ContactStatus;
|
class ContactStatus;
|
||||||
|
class BusinessBotStatus;
|
||||||
class Element;
|
class Element;
|
||||||
class PinnedTracker;
|
class PinnedTracker;
|
||||||
class TranslateBar;
|
class TranslateBar;
|
||||||
|
@ -744,6 +745,7 @@ private:
|
||||||
bool _isInlineBot = false;
|
bool _isInlineBot = false;
|
||||||
|
|
||||||
std::unique_ptr<HistoryView::ContactStatus> _contactStatus;
|
std::unique_ptr<HistoryView::ContactStatus> _contactStatus;
|
||||||
|
std::unique_ptr<HistoryView::BusinessBotStatus> _businessBotStatus;
|
||||||
|
|
||||||
const std::shared_ptr<Ui::SendButton> _send;
|
const std::shared_ptr<Ui::SendButton> _send;
|
||||||
object_ptr<Ui::FlatButton> _unblock;
|
object_ptr<Ui::FlatButton> _unblock;
|
||||||
|
|
|
@ -8,9 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_contact_status.h"
|
#include "history/view/history_view_contact_status.h"
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/controls/userpic_button.h"
|
||||||
|
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
|
@ -18,7 +21,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
|
#include "data/business/data_business_chatbots.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
@ -839,6 +844,241 @@ void ContactStatus::hide() {
|
||||||
_bar.hide();
|
_bar.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BusinessBotStatus::Bar final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
Bar(QWidget *parent);
|
||||||
|
|
||||||
|
void showState(State state);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<> pauseClicks() const;
|
||||||
|
[[nodiscard]] rpl::producer<> resumeClicks() const;
|
||||||
|
[[nodiscard]] rpl::producer<> removeClicks() const;
|
||||||
|
[[nodiscard]] rpl::producer<> manageClicks() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
void showMenu();
|
||||||
|
|
||||||
|
object_ptr<Ui::UserpicButton> _userpic = { nullptr };
|
||||||
|
object_ptr<Ui::FlatLabel> _name;
|
||||||
|
object_ptr<Ui::FlatLabel> _status;
|
||||||
|
object_ptr<Ui::RoundButton> _togglePaused;
|
||||||
|
object_ptr<Ui::IconButton> _settings;
|
||||||
|
rpl::event_stream<> _removeClicks;
|
||||||
|
rpl::event_stream<> _manageClicks;
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
bool _paused = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
BusinessBotStatus::Bar::Bar(QWidget *parent)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _name(this, st::historyBusinessBotName)
|
||||||
|
, _status(this, st::historyBusinessBotStatus)
|
||||||
|
, _togglePaused(
|
||||||
|
this,
|
||||||
|
rpl::single(QString()),
|
||||||
|
st::historyBusinessBotToggle)
|
||||||
|
, _settings(this, st::historyBusinessBotSettings) {
|
||||||
|
_name->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
_status->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
_settings->setClickedCallback([=] {
|
||||||
|
showMenu();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessBotStatus::Bar::showState(State state) {
|
||||||
|
Expects(state.bot != nullptr);
|
||||||
|
|
||||||
|
_userpic = object_ptr<Ui::UserpicButton>(
|
||||||
|
this,
|
||||||
|
state.bot,
|
||||||
|
st::historyBusinessBotPhoto);
|
||||||
|
_userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
_userpic->show();
|
||||||
|
_name->setText(state.bot->name());
|
||||||
|
_status->setText(!state.canReply
|
||||||
|
? tr::lng_chatbot_status_views(tr::now)
|
||||||
|
: state.paused
|
||||||
|
? tr::lng_chatbot_status_paused(tr::now)
|
||||||
|
: tr::lng_chatbot_status_can_reply(tr::now));
|
||||||
|
_togglePaused->setText(state.paused
|
||||||
|
? tr::lng_chatbot_button_resume()
|
||||||
|
: tr::lng_chatbot_button_pause());
|
||||||
|
_togglePaused->setVisible(state.canReply);
|
||||||
|
_paused = state.paused;
|
||||||
|
resizeToWidth(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> BusinessBotStatus::Bar::pauseClicks() const {
|
||||||
|
return _togglePaused->clicks() | rpl::filter([=] {
|
||||||
|
return !_paused;
|
||||||
|
}) | rpl::to_empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> BusinessBotStatus::Bar::resumeClicks() const {
|
||||||
|
return _togglePaused->clicks() | rpl::filter([=] {
|
||||||
|
return _paused;
|
||||||
|
}) | rpl::to_empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> BusinessBotStatus::Bar::removeClicks() const {
|
||||||
|
return _removeClicks.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> BusinessBotStatus::Bar::manageClicks() const {
|
||||||
|
return _manageClicks.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessBotStatus::Bar::showMenu() {
|
||||||
|
if (_menu) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
this,
|
||||||
|
st::popupMenuExpandedSeparator);
|
||||||
|
_menu->setDestroyedCallback([
|
||||||
|
weak = Ui::MakeWeak(this),
|
||||||
|
weakButton = Ui::MakeWeak(_settings.data()),
|
||||||
|
menu = _menu.get()] {
|
||||||
|
if (weak && weak->_menu == menu) {
|
||||||
|
if (weakButton) {
|
||||||
|
weakButton->setForceRippled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_settings->setForceRippled(true);
|
||||||
|
|
||||||
|
const auto addAction = Ui::Menu::CreateAddActionCallback(_menu);
|
||||||
|
|
||||||
|
addAction(tr::lng_chatbot_menu_manage(tr::now), crl::guard(this, [=] {
|
||||||
|
_manageClicks.fire({});
|
||||||
|
}), &st::menuIconSettings);
|
||||||
|
addAction({
|
||||||
|
.text = (_togglePaused->isHidden()
|
||||||
|
? tr::lng_chatbot_menu_revoke(tr::now)
|
||||||
|
: tr::lng_chatbot_menu_remove(tr::now)),
|
||||||
|
.handler = crl::guard(this, [=] { _removeClicks.fire({}); }),
|
||||||
|
.icon = &st::menuIconDisableAttention,
|
||||||
|
.isAttention = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
|
||||||
|
_menu->popup(mapToGlobal(QPoint(
|
||||||
|
width() + st::topBarMenuPosition.x(),
|
||||||
|
st::topBarMenuPosition.y())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessBotStatus::Bar::paintEvent(QPaintEvent *e) {
|
||||||
|
QPainter p(this);
|
||||||
|
p.fillRect(e->rect(), st::historyContactStatusButton.bgColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BusinessBotStatus::Bar::resizeGetHeight(int newWidth) {
|
||||||
|
const auto &st = st::defaultPeerList.item;
|
||||||
|
_settings->moveToRight(0, 0, newWidth);
|
||||||
|
if (_userpic) {
|
||||||
|
_userpic->moveToLeft(st.photoPosition.x(), st.photoPosition.y());
|
||||||
|
}
|
||||||
|
auto available = newWidth - _settings->width() - st.namePosition.x();
|
||||||
|
if (!_togglePaused->isHidden()) {
|
||||||
|
_togglePaused->moveToRight(_settings->width(), 0);
|
||||||
|
available -= _togglePaused->width();
|
||||||
|
}
|
||||||
|
_name->resizeToWidth(available);
|
||||||
|
_name->moveToLeft(st.namePosition.x(), st.namePosition.y());
|
||||||
|
_status->resizeToWidth(available);
|
||||||
|
_status->moveToLeft(st.statusPosition.x(), st.statusPosition.y());
|
||||||
|
return st.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
BusinessBotStatus::BusinessBotStatus(
|
||||||
|
not_null<Window::SessionController*> window,
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
not_null<PeerData*> peer)
|
||||||
|
: _controller(window)
|
||||||
|
, _inner(Ui::CreateChild<Bar>(parent.get()))
|
||||||
|
, _bar(parent, object_ptr<Bar>::fromRaw(_inner)) {
|
||||||
|
setupState(peer);
|
||||||
|
setupHandlers(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto BusinessBotStatus::PeerState(not_null<PeerData*> peer)
|
||||||
|
-> rpl::producer<State> {
|
||||||
|
using SettingsChange = PeerData::BarSettings::Change;
|
||||||
|
return peer->barSettingsValue(
|
||||||
|
) | rpl::map([=](SettingsChange settings) -> State {
|
||||||
|
using Flag = PeerBarSetting;
|
||||||
|
return {
|
||||||
|
.bot = peer->businessBot(),
|
||||||
|
.manageUrl = peer->businessBotManageUrl(),
|
||||||
|
.canReply = ((settings.value & Flag::BusinessBotCanReply) != 0),
|
||||||
|
.paused = ((settings.value & Flag::BusinessBotPaused) != 0),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessBotStatus::setupState(not_null<PeerData*> peer) {
|
||||||
|
if (!BarCurrentlyHidden(peer)) {
|
||||||
|
peer->session().api().requestPeerSettings(peer);
|
||||||
|
}
|
||||||
|
PeerState(
|
||||||
|
peer
|
||||||
|
) | rpl::start_with_next([=](State state) {
|
||||||
|
_state = state;
|
||||||
|
if (!state.bot) {
|
||||||
|
_bar.toggleContent(false);
|
||||||
|
} else {
|
||||||
|
_inner->showState(state);
|
||||||
|
_bar.toggleContent(true);
|
||||||
|
}
|
||||||
|
}, _bar.lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessBotStatus::setupHandlers(not_null<PeerData*> peer) {
|
||||||
|
_inner->pauseClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
peer->owner().chatbots().togglePaused(peer, true);
|
||||||
|
}, _bar.lifetime());
|
||||||
|
|
||||||
|
_inner->resumeClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
peer->owner().chatbots().togglePaused(peer, false);
|
||||||
|
}, _bar.lifetime());
|
||||||
|
|
||||||
|
_inner->removeClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
peer->owner().chatbots().removeFrom(peer);
|
||||||
|
}, _bar.lifetime());
|
||||||
|
|
||||||
|
_inner->manageClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
UrlClickHandler::Open(
|
||||||
|
_state.manageUrl,
|
||||||
|
QVariant::fromValue(ClickHandlerContext{
|
||||||
|
.sessionWindow = base::make_weak(_controller),
|
||||||
|
.botStartAutoSubmit = true,
|
||||||
|
}));
|
||||||
|
}, _bar.lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessBotStatus::show() {
|
||||||
|
if (!_shown) {
|
||||||
|
_shown = true;
|
||||||
|
if (_state.bot) {
|
||||||
|
_inner->showState(_state);
|
||||||
|
_bar.toggleContent(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_bar.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessBotStatus::hide() {
|
||||||
|
_bar.hide();
|
||||||
|
}
|
||||||
|
|
||||||
TopicReopenBar::TopicReopenBar(
|
TopicReopenBar::TopicReopenBar(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
not_null<Data::ForumTopic*> topic)
|
not_null<Data::ForumTopic*> topic)
|
||||||
|
|
|
@ -124,6 +124,43 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BusinessBotStatus final {
|
||||||
|
public:
|
||||||
|
BusinessBotStatus(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
void show();
|
||||||
|
void hide();
|
||||||
|
|
||||||
|
[[nodiscard]] SlidingBar &bar() {
|
||||||
|
return _bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Bar;
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
UserData *bot = nullptr;
|
||||||
|
QString manageUrl;
|
||||||
|
bool canReply = false;
|
||||||
|
bool paused = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void setupState(not_null<PeerData*> peer);
|
||||||
|
void setupHandlers(not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
static rpl::producer<State> PeerState(not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
const not_null<Window::SessionController*> _controller;
|
||||||
|
State _state;
|
||||||
|
QPointer<Bar> _inner;
|
||||||
|
SlidingBar _bar;
|
||||||
|
bool _shown = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class TopicReopenBar final {
|
class TopicReopenBar final {
|
||||||
public:
|
public:
|
||||||
TopicReopenBar(
|
TopicReopenBar(
|
||||||
|
|
|
@ -1704,7 +1704,7 @@ inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut;
|
||||||
messages.quickReplies#c68d6695 quick_replies:Vector<QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
|
messages.quickReplies#c68d6695 quick_replies:Vector<QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
|
||||||
messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;
|
messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;
|
||||||
|
|
||||||
connectedBot#e7e999e7 flags:# can_reply:flags.0?true bot_id:long recipients:BusinessRecipients = ConnectedBot;
|
connectedBot#bd068601 flags:# can_reply:flags.0?true bot_id:long recipients:BusinessBotRecipients = ConnectedBot;
|
||||||
|
|
||||||
account.connectedBots#17d7f87b connected_bots:Vector<ConnectedBot> users:Vector<User> = account.ConnectedBots;
|
account.connectedBots#17d7f87b connected_bots:Vector<ConnectedBot> users:Vector<User> = account.ConnectedBots;
|
||||||
|
|
||||||
|
@ -1723,6 +1723,10 @@ inputCollectiblePhone#a2e214a4 phone:string = InputCollectible;
|
||||||
|
|
||||||
fragment.collectibleInfo#6ebdff91 purchase_date:int currency:string amount:long crypto_currency:string crypto_amount:long url:string = fragment.CollectibleInfo;
|
fragment.collectibleInfo#6ebdff91 purchase_date:int currency:string amount:long crypto_currency:string crypto_amount:long url:string = fragment.CollectibleInfo;
|
||||||
|
|
||||||
|
inputBusinessBotRecipients#c4e5921e flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<InputUser> exclude_users:flags.6?Vector<InputUser> = InputBusinessBotRecipients;
|
||||||
|
|
||||||
|
businessBotRecipients#b88cf373 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<long> exclude_users:flags.6?Vector<long> = BusinessBotRecipients;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
|
@ -1853,7 +1857,7 @@ account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?Bus
|
||||||
account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool;
|
account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool;
|
||||||
account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool;
|
account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool;
|
||||||
account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool;
|
account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool;
|
||||||
account.updateConnectedBot#9c2d527d flags:# can_reply:flags.0?true deleted:flags.1?true bot:InputUser recipients:InputBusinessRecipients = Updates;
|
account.updateConnectedBot#43d8521d flags:# can_reply:flags.0?true deleted:flags.1?true bot:InputUser recipients:InputBusinessBotRecipients = Updates;
|
||||||
account.getConnectedBots#4ea4c80f = account.ConnectedBots;
|
account.getConnectedBots#4ea4c80f = account.ConnectedBots;
|
||||||
account.getBotBusinessConnection#76a86270 connection_id:string = Updates;
|
account.getBotBusinessConnection#76a86270 connection_id:string = Updates;
|
||||||
account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool;
|
account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool;
|
||||||
|
|
|
@ -336,6 +336,7 @@ void AwayMessage::setupContent(
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.title = tr::lng_away_recipients(),
|
.title = tr::lng_away_recipients(),
|
||||||
.data = &_recipients,
|
.data = &_recipients,
|
||||||
|
.type = Data::BusinessRecipientsType::Messages,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ui::AddSkip(inner, st::settingsChatbotsAccessSkip);
|
Ui::AddSkip(inner, st::settingsChatbotsAccessSkip);
|
||||||
|
|
|
@ -448,6 +448,7 @@ void Chatbots::setupContent(
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.title = tr::lng_chatbots_access_title(),
|
.title = tr::lng_chatbots_access_title(),
|
||||||
.data = &_recipients,
|
.data = &_recipients,
|
||||||
|
.type = Data::BusinessRecipientsType::Bots,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
|
Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
|
||||||
|
|
|
@ -229,6 +229,7 @@ void Greeting::setupContent(
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.title = tr::lng_greeting_recipients(),
|
.title = tr::lng_greeting_recipients(),
|
||||||
.data = &_recipients,
|
.data = &_recipients,
|
||||||
|
.type = Data::BusinessRecipientsType::Messages,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ui::AddSkip(inner);
|
Ui::AddSkip(inner);
|
||||||
|
|
|
@ -69,7 +69,7 @@ void EditBusinessChats(
|
||||||
(descriptor.include
|
(descriptor.include
|
||||||
? tr::lng_filters_include_title()
|
? tr::lng_filters_include_title()
|
||||||
: tr::lng_filters_exclude_title()),
|
: tr::lng_filters_exclude_title()),
|
||||||
options,
|
(descriptor.usersOnly ? Flag() : options),
|
||||||
TypesToFlags(descriptor.current.types) & options,
|
TypesToFlags(descriptor.current.types) & options,
|
||||||
base::flat_set<not_null<History*>>(begin(peers), end(peers)),
|
base::flat_set<not_null<History*>>(begin(peers), end(peers)),
|
||||||
100,
|
100,
|
||||||
|
@ -162,6 +162,8 @@ void AddBusinessRecipientsSelector(
|
||||||
auto &lifetime = container->lifetime();
|
auto &lifetime = container->lifetime();
|
||||||
const auto controller = descriptor.controller;
|
const auto controller = descriptor.controller;
|
||||||
const auto data = descriptor.data;
|
const auto data = descriptor.data;
|
||||||
|
const auto includeWithExcluded = (descriptor.type
|
||||||
|
== Data::BusinessRecipientsType::Bots);
|
||||||
const auto change = [=](Fn<void(Data::BusinessRecipients&)> modify) {
|
const auto change = [=](Fn<void(Data::BusinessRecipients&)> modify) {
|
||||||
auto now = data->current();
|
auto now = data->current();
|
||||||
modify(now);
|
modify(now);
|
||||||
|
@ -191,11 +193,17 @@ void AddBusinessRecipientsSelector(
|
||||||
Ui::AddSkip(container, st::settingsChatbotsAccessSkip);
|
Ui::AddSkip(container, st::settingsChatbotsAccessSkip);
|
||||||
Ui::AddDivider(container);
|
Ui::AddDivider(container);
|
||||||
|
|
||||||
|
const auto includeWrap = container->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::VerticalLayout>(container))
|
||||||
|
)->setDuration(0);
|
||||||
const auto excludeWrap = container->add(
|
const auto excludeWrap = container->add(
|
||||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
container,
|
container,
|
||||||
object_ptr<Ui::VerticalLayout>(container))
|
object_ptr<Ui::VerticalLayout>(container))
|
||||||
)->setDuration(0);
|
)->setDuration(0);
|
||||||
|
|
||||||
const auto excludeInner = excludeWrap->entity();
|
const auto excludeInner = excludeWrap->entity();
|
||||||
|
|
||||||
Ui::AddSkip(excludeInner);
|
Ui::AddSkip(excludeInner);
|
||||||
|
@ -205,18 +213,34 @@ void AddBusinessRecipientsSelector(
|
||||||
tr::lng_chatbots_exclude_button(),
|
tr::lng_chatbots_exclude_button(),
|
||||||
st::settingsChatbotsAdd,
|
st::settingsChatbotsAdd,
|
||||||
{ &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
|
{ &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
|
||||||
excludeAdd->setClickedCallback([=] {
|
const auto addExcluded = [=] {
|
||||||
const auto save = [=](Data::BusinessChats value) {
|
const auto save = [=](Data::BusinessChats value) {
|
||||||
change([&](Data::BusinessRecipients &data) {
|
change([&](Data::BusinessRecipients &data) {
|
||||||
|
if (includeWithExcluded) {
|
||||||
|
if (!data.allButExcluded) {
|
||||||
|
value.types = {};
|
||||||
|
}
|
||||||
|
for (const auto &user : value.list) {
|
||||||
|
data.included.list.erase(
|
||||||
|
ranges::remove(data.included.list, user),
|
||||||
|
end(data.included.list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!value.empty()) {
|
||||||
|
data.included = {};
|
||||||
|
}
|
||||||
data.excluded = std::move(value);
|
data.excluded = std::move(value);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
EditBusinessChats(controller, {
|
EditBusinessChats(controller, {
|
||||||
.current = data->current().excluded,
|
.current = data->current().excluded,
|
||||||
.save = crl::guard(excludeAdd, save),
|
.save = crl::guard(excludeAdd, save),
|
||||||
|
.usersOnly = (includeWithExcluded
|
||||||
|
&& !data->current().allButExcluded),
|
||||||
.include = false,
|
.include = false,
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
excludeAdd->setClickedCallback(addExcluded);
|
||||||
|
|
||||||
const auto excluded = lifetime.make_state<
|
const auto excluded = lifetime.make_state<
|
||||||
rpl::variable<Data::BusinessChats>
|
rpl::variable<Data::BusinessChats>
|
||||||
|
@ -227,24 +251,19 @@ void AddBusinessRecipientsSelector(
|
||||||
}, lifetime);
|
}, lifetime);
|
||||||
excluded->changes(
|
excluded->changes(
|
||||||
) | rpl::start_with_next([=](Data::BusinessChats &&value) {
|
) | rpl::start_with_next([=](Data::BusinessChats &&value) {
|
||||||
auto now = data->current();
|
change([&](Data::BusinessRecipients &data) {
|
||||||
now.excluded = std::move(value);
|
data.excluded = std::move(value);
|
||||||
*data = std::move(now);
|
});
|
||||||
}, lifetime);
|
}, lifetime);
|
||||||
|
|
||||||
SetupBusinessChatsPreview(excludeInner, excluded);
|
SetupBusinessChatsPreview(excludeInner, excluded);
|
||||||
|
|
||||||
excludeWrap->toggleOn(data->value(
|
excludeWrap->toggleOn(data->value(
|
||||||
) | rpl::map([](const Data::BusinessRecipients &value) {
|
) | rpl::map([=](const Data::BusinessRecipients &value) {
|
||||||
return value.allButExcluded;
|
return value.allButExcluded || includeWithExcluded;
|
||||||
}));
|
}));
|
||||||
excludeWrap->finishAnimating();
|
excludeWrap->finishAnimating();
|
||||||
|
|
||||||
const auto includeWrap = container->add(
|
|
||||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
|
||||||
container,
|
|
||||||
object_ptr<Ui::VerticalLayout>(container))
|
|
||||||
)->setDuration(0);
|
|
||||||
const auto includeInner = includeWrap->entity();
|
const auto includeInner = includeWrap->entity();
|
||||||
|
|
||||||
Ui::AddSkip(includeInner);
|
Ui::AddSkip(includeInner);
|
||||||
|
@ -254,18 +273,32 @@ void AddBusinessRecipientsSelector(
|
||||||
tr::lng_chatbots_include_button(),
|
tr::lng_chatbots_include_button(),
|
||||||
st::settingsChatbotsAdd,
|
st::settingsChatbotsAdd,
|
||||||
{ &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
|
{ &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
|
||||||
includeAdd->setClickedCallback([=] {
|
const auto addIncluded = [=] {
|
||||||
const auto save = [=](Data::BusinessChats value) {
|
const auto save = [=](Data::BusinessChats value) {
|
||||||
change([&](Data::BusinessRecipients &data) {
|
change([&](Data::BusinessRecipients &data) {
|
||||||
|
if (includeWithExcluded) {
|
||||||
|
for (const auto &user : value.list) {
|
||||||
|
data.excluded.list.erase(
|
||||||
|
ranges::remove(data.excluded.list, user),
|
||||||
|
end(data.excluded.list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!value.empty()) {
|
||||||
|
data.excluded.types = {};
|
||||||
|
}
|
||||||
data.included = std::move(value);
|
data.included = std::move(value);
|
||||||
});
|
});
|
||||||
|
if (!data->current().included.empty()) {
|
||||||
|
group->setValue(kSelectedOnly);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
EditBusinessChats(controller, {
|
EditBusinessChats(controller, {
|
||||||
.current = data->current().included ,
|
.current = data->current().included,
|
||||||
.save = crl::guard(includeAdd, save),
|
.save = crl::guard(includeAdd, save),
|
||||||
.include = true,
|
.include = true,
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
includeAdd->setClickedCallback(addIncluded);
|
||||||
|
|
||||||
const auto included = lifetime.make_state<
|
const auto included = lifetime.make_state<
|
||||||
rpl::variable<Data::BusinessChats>
|
rpl::variable<Data::BusinessChats>
|
||||||
|
@ -298,16 +331,7 @@ void AddBusinessRecipientsSelector(
|
||||||
group->setChangedCallback([=](int value) {
|
group->setChangedCallback([=](int value) {
|
||||||
if (value == kSelectedOnly && data->current().included.empty()) {
|
if (value == kSelectedOnly && data->current().included.empty()) {
|
||||||
group->setValue(kAllExcept);
|
group->setValue(kAllExcept);
|
||||||
const auto save = [=](Data::BusinessChats value) {
|
addIncluded();
|
||||||
change([&](Data::BusinessRecipients &data) {
|
|
||||||
data.included = std::move(value);
|
|
||||||
});
|
|
||||||
group->setValue(kSelectedOnly);
|
|
||||||
};
|
|
||||||
EditBusinessChats(controller, {
|
|
||||||
.save = crl::guard(includeAdd, save),
|
|
||||||
.include = true,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
change([&](Data::BusinessRecipients &data) {
|
change([&](Data::BusinessRecipients &data) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/required.h"
|
||||||
#include "data/business/data_business_common.h"
|
#include "data/business/data_business_common.h"
|
||||||
#include "settings/settings_common_session.h"
|
#include "settings/settings_common_session.h"
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ private:
|
||||||
struct BusinessChatsDescriptor {
|
struct BusinessChatsDescriptor {
|
||||||
Data::BusinessChats current;
|
Data::BusinessChats current;
|
||||||
Fn<void(const Data::BusinessChats&)> save;
|
Fn<void(const Data::BusinessChats&)> save;
|
||||||
|
bool usersOnly = false;
|
||||||
bool include = false;
|
bool include = false;
|
||||||
};
|
};
|
||||||
void EditBusinessChats(
|
void EditBusinessChats(
|
||||||
|
@ -66,6 +68,7 @@ struct BusinessRecipientsSelectorDescriptor {
|
||||||
not_null<Window::SessionController*> controller;
|
not_null<Window::SessionController*> controller;
|
||||||
rpl::producer<QString> title;
|
rpl::producer<QString> title;
|
||||||
not_null<rpl::variable<Data::BusinessRecipients>*> data;
|
not_null<rpl::variable<Data::BusinessRecipients>*> data;
|
||||||
|
base::required<Data::BusinessRecipientsType> type;
|
||||||
};
|
};
|
||||||
void AddBusinessRecipientsSelector(
|
void AddBusinessRecipientsSelector(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
|
Loading…
Add table
Reference in a new issue