diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index e2bac97dd1..7dfd2490cf 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -163,6 +163,8 @@ PRIVATE boxes/filters/edit_filter_box.h boxes/filters/edit_filter_chats_list.cpp boxes/filters/edit_filter_chats_list.h + boxes/peers/add_bot_to_chat_box.cpp + boxes/peers/add_bot_to_chat_box.h boxes/peers/add_participants_box.cpp boxes/peers/add_participants_box.h boxes/peers/edit_contact_box.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 35a9597b7f..5a1fcc90b6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1714,6 +1714,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_bot_channels_manage" = "Channels I manage"; "lng_bot_groups" = "Groups"; "lng_bot_add_title" = "Add Bot"; +"lng_bot_as_admin_check" = "Admin rights"; "lng_bot_add_as_admin" = "Add Bot as Admin"; "lng_bot_add_as_member" = "Add Bot as Member"; "lng_bot_sure_add_title" = "Add bot as admin"; diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index fefe35124a..be1a118853 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -89,35 +89,6 @@ private: }; -[[nodiscard]] object_ptr CreateSectionSubtitle( - not_null parent, - rpl::producer text) { - auto result = object_ptr( - parent, - st::searchedBarHeight); - - const auto raw = result.data(); - raw->paintRequest( - ) | rpl::start_with_next([=](QRect clip) { - auto p = QPainter(raw); - p.fillRect(clip, st::searchedBarBg); - }, raw->lifetime()); - - const auto label = Ui::CreateChild( - raw, - std::move(text), - st::windowFilterChatsSectionSubtitle); - raw->widthValue( - ) | rpl::start_with_next([=](int width) { - const auto padding = st::windowFilterChatsSectionSubtitlePadding; - const auto available = width - padding.left() - padding.right(); - label->resizeToNaturalWidth(available); - label->moveToLeft(padding.left(), padding.top(), width); - }, label->lifetime()); - - return result; -} - [[nodiscard]] uint64 TypeId(Flag flag) { return PeerId(FakeChatId(static_cast(flag))).value; } @@ -291,6 +262,35 @@ void PaintFilterChatsTypeIcon( icon.paintInCenter(p, rect); } +object_ptr CreatePeerListSectionSubtitle( + not_null parent, + rpl::producer text) { + auto result = object_ptr( + parent, + st::searchedBarHeight); + + const auto raw = result.data(); + raw->paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + auto p = QPainter(raw); + p.fillRect(clip, st::searchedBarBg); + }, raw->lifetime()); + + const auto label = Ui::CreateChild( + raw, + std::move(text), + st::windowFilterChatsSectionSubtitle); + raw->widthValue( + ) | rpl::start_with_next([=](int width) { + const auto padding = st::windowFilterChatsSectionSubtitlePadding; + const auto available = width - padding.left() - padding.right(); + label->resizeToNaturalWidth(available); + label->moveToLeft(padding.left(), padding.top(), width); + }, label->lifetime()); + + return result; +} + EditFilterChatsListController::EditFilterChatsListController( not_null session, rpl::producer title, @@ -357,7 +357,7 @@ void EditFilterChatsListController::prepareViewHook() { object_ptr EditFilterChatsListController::prepareTypesList() { auto result = object_ptr((QWidget*)nullptr); const auto container = result.data(); - container->add(CreateSectionSubtitle( + container->add(CreatePeerListSectionSubtitle( container, tr::lng_filters_edit_types())); container->add(object_ptr( @@ -390,7 +390,7 @@ object_ptr EditFilterChatsListController::prepareTypesList() { container->add(object_ptr( container, st::membersMarginBottom)); - container->add(CreateSectionSubtitle( + container->add(CreatePeerListSectionSubtitle( container, tr::lng_filters_edit_chats())); diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h index 4dc777eb2a..dd39baf9cc 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h @@ -35,6 +35,10 @@ void PaintFilterChatsTypeIcon( int outerWidth, int size); +[[nodiscard]] object_ptr CreatePeerListSectionSubtitle( + not_null parent, + rpl::producer text); + class EditFilterChatsListController final : public ChatsListBoxController { public: using Flag = Data::ChatFilter::Flag; diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index ba30820253..4edf1738f3 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -25,8 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "lang/lang_keys.h" #include "history/history.h" -#include "boxes/peers/edit_participant_box.h" -#include "boxes/peers/edit_participants_box.h" #include "dialogs/dialogs_main_list.h" #include "window/window_session_controller.h" // showAddContact() #include "base/unixtime.h" @@ -38,52 +36,6 @@ namespace { constexpr auto kSortByOnlineThrottle = 3 * crl::time(1000); -void ShareBotGame(not_null bot, not_null chat) { - const auto history = chat->owner().history(chat); - auto &histories = history->owner().histories(); - const auto requestType = Data::Histories::RequestType::Send; - histories.sendRequest(history, requestType, [=](Fn finish) { - const auto randomId = base::RandomValue(); - const auto api = &chat->session().api(); - history->sendRequestId = api->request(MTPmessages_SendMedia( - MTP_flags(0), - chat->input, - MTP_int(0), - MTP_inputMediaGame( - MTP_inputGameShortName( - bot->inputUser, - MTP_string(bot->botInfo->shareGameShortName))), - MTP_string(), - MTP_long(randomId), - MTPReplyMarkup(), - MTPVector(), - MTP_int(0), // schedule_date - MTPInputPeer() // send_as - )).done([=](const MTPUpdates &result) { - api->applyUpdates(result, randomId); - finish(); - }).fail([=](const MTP::Error &error) { - api->sendMessageFail(error, chat); - finish(); - }).afterRequest( - history->sendRequestId - ).send(); - return history->sendRequestId; - }); - Ui::hideLayer(); - Ui::showPeerHistory(chat, ShowAtUnreadMsgId); -} - -void AddBotToGroup(not_null bot, not_null chat) { - if (bot->isBot() && !bot->botInfo->startGroupToken.isEmpty()) { - chat->session().api().sendBotStart(bot, chat); - } else { - chat->session().api().chatParticipants().add(chat, { 1, bot }); - } - Ui::hideLayer(); - Ui::showPeerHistory(chat, ShowAtUnreadMsgId); -} - } // namespace // Not used for now. @@ -526,168 +478,6 @@ std::unique_ptr ContactsBoxController::createRow( return std::make_unique(user); } -void AddBotToGroupBoxController::Start(not_null bot) { - auto initBox = [=](not_null box) { - box->addButton(tr::lng_cancel(), [box] { box->closeBox(); }); - }; - Ui::show(Box( - std::make_unique(bot), - std::move(initBox))); -} - -AddBotToGroupBoxController::AddBotToGroupBoxController( - not_null bot) -: ChatsListBoxController(SharingBotGame(bot) - ? std::make_unique(&bot->session()) - : nullptr) -, _bot(bot) { -} - -Main::Session &AddBotToGroupBoxController::session() const { - return _bot->session(); -} - -void AddBotToGroupBoxController::rowClicked(not_null row) { - if (sharingBotGame()) { - shareBotGame(row->peer()); - } else { - addBotToGroup(row->peer()); - } -} - -void AddBotToGroupBoxController::shareBotGame(not_null chat) { - auto send = crl::guard(this, [bot = _bot, chat] { - ShareBotGame(bot, chat); - }); - auto confirmText = [chat] { - if (chat->isUser()) { - return tr::lng_bot_sure_share_game(tr::now, lt_user, chat->name); - } - return tr::lng_bot_sure_share_game_group(tr::now, lt_group, chat->name); - }(); - Ui::show( - Ui::MakeConfirmBox({ - .text = confirmText, - .confirmed = std::move(send), - }), - Ui::LayerOption::KeepOther); -} - -void AddBotToGroupBoxController::addBotToGroup(not_null chat) { - if (const auto megagroup = chat->asMegagroup()) { - if (!megagroup->canAddMembers()) { - Ui::show( - Ui::MakeInformBox(tr::lng_error_cant_add_member()), - Ui::LayerOption::KeepOther); - return; - } - } - const auto bot = _bot; - const auto close = [=](auto&&...) { - Ui::hideLayer(); - Ui::showPeerHistory(chat, ShowAtUnreadMsgId); - }; - const auto saveCallback = SaveAdminCallback( - chat, - bot, - close, - close); - auto box = object_ptr(nullptr); - if (chat->isBroadcast()) { - if (bot->botInfo->channelAdminRights) { - box = Box( - chat, - bot, - ChatAdminRightsInfo(bot->botInfo->channelAdminRights), - QString()); - } - } else if (bot->botInfo->groupAdminRights) { - box = Box( - chat, - bot, - ChatAdminRightsInfo(bot->botInfo->groupAdminRights), - QString()); - } - if (box) { - box->setSaveCallback(saveCallback); - Ui::show(std::move(box)); - return; - } - Ui::show( - Ui::MakeConfirmBox({ - tr::lng_bot_sure_invite(tr::now, lt_group, chat->name), - crl::guard(this, [=] { AddBotToGroup(bot, chat); }), - }), - Ui::LayerOption::KeepOther); -} - -auto AddBotToGroupBoxController::createRow(not_null history) --> std::unique_ptr { - if (!needToCreateRow(history->peer)) { - return nullptr; - } - return std::make_unique(history); -} - -bool AddBotToGroupBoxController::needToCreateRow( - not_null peer) const { - if (sharingBotGame()) { - if (!peer->canWrite() - || peer->amRestricted(ChatRestriction::SendGames)) { - return false; - } - return true; - } - if (const auto chat = peer->asChat()) { - return chat->canAddMembers(); - } else if (const auto group = peer->asMegagroup()) { - return group->canAddMembers(); - } - return false; -} - -bool AddBotToGroupBoxController::SharingBotGame(not_null bot) { - const auto &info = bot->botInfo; - return (info && !info->shareGameShortName.isEmpty()); -} - -bool AddBotToGroupBoxController::sharingBotGame() const { - return SharingBotGame(_bot); -} - -QString AddBotToGroupBoxController::emptyBoxText() const { - return !session().data().chatsListLoaded() - ? tr::lng_contacts_loading(tr::now) - : sharingBotGame() - ? tr::lng_bot_no_chats(tr::now) - : tr::lng_bot_no_groups(tr::now); -} - -QString AddBotToGroupBoxController::noResultsText() const { - return !session().data().chatsListLoaded() - ? tr::lng_contacts_loading(tr::now) - : sharingBotGame() - ? tr::lng_bot_chats_not_found(tr::now) - : tr::lng_bot_groups_not_found(tr::now); -} - -void AddBotToGroupBoxController::updateLabels() { - setSearchNoResultsText(noResultsText()); -} - -void AddBotToGroupBoxController::prepareViewHook() { - delegate()->peerListSetTitle(sharingBotGame() - ? tr::lng_bot_choose_chat() - : tr::lng_bot_choose_group()); - updateLabels(); - session().data().chatsListLoadedEvents( - ) | rpl::filter([=](Data::Folder *folder) { - return !folder; - }) | rpl::start_with_next([=] { - updateLabels(); - }, lifetime()); -} - ChooseRecipientBoxController::ChooseRecipientBoxController( not_null session, FnMut)> callback) diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index a9599d8f97..21d0043c92 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -165,38 +165,6 @@ private: }; -class AddBotToGroupBoxController - : public ChatsListBoxController - , public base::has_weak_ptr { -public: - static void Start(not_null bot); - - explicit AddBotToGroupBoxController(not_null bot); - - Main::Session &session() const override; - void rowClicked(not_null row) override; - -protected: - std::unique_ptr createRow(not_null history) override; - void prepareViewHook() override; - QString emptyBoxText() const override; - -private: - static bool SharingBotGame(not_null bot); - - bool needToCreateRow(not_null peer) const; - bool sharingBotGame() const; - QString noResultsText() const; - QString descriptionText() const; - void updateLabels(); - - void shareBotGame(not_null chat); - void addBotToGroup(not_null chat); - - const not_null _bot; - -}; - class ChooseRecipientBoxController : public ChatsListBoxController , public base::has_weak_ptr { diff --git a/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.cpp new file mode 100644 index 0000000000..c6f182adc6 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.cpp @@ -0,0 +1,381 @@ +/* +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 "boxes/peers/add_bot_to_chat_box.h" + +#include "lang/lang_keys.h" +#include "data/data_user.h" +#include "data/data_chat.h" +#include "data/data_channel.h" +#include "data/data_session.h" +#include "data/data_histories.h" +#include "history/history.h" +#include "main/main_session.h" +#include "boxes/peers/edit_participant_box.h" +#include "boxes/peers/edit_participants_box.h" +#include "boxes/filters/edit_filter_chats_list.h" +#include "ui/boxes/confirm_box.h" +#include "base/random.h" +#include "base/weak_ptr.h" +#include "api/api_chat_participants.h" +#include "apiwrap.h" +#include "facades.h" +#include "styles/style_boxes.h" + +namespace { + +class Controller final + : public PeerListController + , public base::has_weak_ptr { +public: + Controller( + not_null session, + rpl::producer> add, + Fn chat)> callback); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + +private: + void addRow(not_null peer); + + const not_null _session; + Fn chat)> _callback; + std::vector> _list; + bool _prepared = false; + bool _refreshing = false; + + rpl::lifetime _lifetime; + +}; + +void ShareBotGame(not_null bot, not_null chat) { + const auto history = chat->owner().history(chat); + auto &histories = history->owner().histories(); + const auto requestType = Data::Histories::RequestType::Send; + histories.sendRequest(history, requestType, [=](Fn finish) { + const auto randomId = base::RandomValue(); + const auto api = &chat->session().api(); + history->sendRequestId = api->request(MTPmessages_SendMedia( + MTP_flags(0), + chat->input, + MTP_int(0), + MTP_inputMediaGame( + MTP_inputGameShortName( + bot->inputUser, + MTP_string(bot->botInfo->shareGameShortName))), + MTP_string(), + MTP_long(randomId), + MTPReplyMarkup(), + MTPVector(), + MTP_int(0), // schedule_date + MTPInputPeer() // send_as + )).done([=](const MTPUpdates &result) { + api->applyUpdates(result, randomId); + finish(); + }).fail([=](const MTP::Error &error) { + api->sendMessageFail(error, chat); + finish(); + }).afterRequest( + history->sendRequestId + ).send(); + return history->sendRequestId; + }); + Ui::hideLayer(); + Ui::showPeerHistory(chat, ShowAtUnreadMsgId); +} + +Controller::Controller( + not_null session, + rpl::producer> add, + Fn chat)> callback) +: _session(session) +, _callback(std::move(callback)) { + std::move( + add + ) | rpl::start_with_next([=](not_null peer) { + if (_prepared) { + addRow(peer); + } else { + _list.push_back(peer); + } + }, _lifetime); +} + +Main::Session &Controller::session() const { + return *_session; +} + +void Controller::prepare() { + _prepared = true; + for (const auto &peer : _list) { + addRow(peer); + } +} + +void Controller::rowClicked(not_null row) { + _callback(row->peer()); +} + +void Controller::addRow(not_null peer) { + if (delegate()->peerListFindRow(peer->id.value)) { + return; + } + delegate()->peerListAppendRow(std::make_unique(peer)); + if (!_refreshing) { + _refreshing = true; + Ui::PostponeCall(this, [=] { + _refreshing = false; + delegate()->peerListRefreshRows(); + }); + } +} + +} // namespace + +void AddBotToGroupBoxController::Start(not_null bot) { + auto initBox = [=](not_null box) { + box->addButton(tr::lng_cancel(), [box] { box->closeBox(); }); + }; + Ui::show(Box( + std::make_unique(bot), + std::move(initBox))); +} + +AddBotToGroupBoxController::AddBotToGroupBoxController( + not_null bot) +: ChatsListBoxController(SharingBotGame(bot) + ? std::make_unique(&bot->session()) + : nullptr) +, _bot(bot) +, _adminToGroup(_bot->botInfo->groupAdminRights != 0) +, _adminToChannel(_bot->botInfo->channelAdminRights != 0) { +} + +Main::Session &AddBotToGroupBoxController::session() const { + return _bot->session(); +} + +void AddBotToGroupBoxController::rowClicked(not_null row) { + if (sharingBotGame()) { + shareBotGame(row->peer()); + } else { + addBotToGroup(row->peer()); + } +} + +void AddBotToGroupBoxController::shareBotGame(not_null chat) { + auto send = crl::guard(this, [bot = _bot, chat] { + ShareBotGame(bot, chat); + }); + auto confirmText = [chat] { + if (chat->isUser()) { + return tr::lng_bot_sure_share_game(tr::now, lt_user, chat->name); + } + return tr::lng_bot_sure_share_game_group(tr::now, lt_group, chat->name); + }(); + Ui::show( + Ui::MakeConfirmBox({ + .text = confirmText, + .confirmed = std::move(send), + }), + Ui::LayerOption::KeepOther); +} + +void AddBotToGroupBoxController::addBotToGroup(not_null chat) { + if (const auto megagroup = chat->asMegagroup()) { + if (!megagroup->canAddMembers()) { + Ui::show( + Ui::MakeInformBox(tr::lng_error_cant_add_member()), + Ui::LayerOption::KeepOther); + return; + } + } + const auto bot = _bot; + const auto close = [=](auto&&...) { + Ui::hideLayer(); + Ui::showPeerHistory(chat, ShowAtUnreadMsgId); + }; + const auto rights = (chat->isBroadcast() + && chat->asBroadcast()->canAddAdmins()) + ? bot->botInfo->channelAdminRights + : ((chat->isMegagroup() && chat->asMegagroup()->canAddAdmins()) + || (chat->isChat() && chat->asChat()->canAddAdmins())) + ? bot->botInfo->groupAdminRights + : ChatAdminRights(); + if (rights) { + const auto saveCallback = SaveAdminCallback( + chat, + bot, + close, + close); + auto box = object_ptr(nullptr); + box = Box( + chat, + bot, + ChatAdminRightsInfo(rights), + QString(), + true); + box->setSaveCallback(saveCallback); + Ui::show(std::move(box), Ui::LayerOption::KeepOther); + } else { + Ui::show( + Ui::MakeConfirmBox({ + tr::lng_bot_sure_invite(tr::now, lt_group, chat->name), + crl::guard(this, [=] { AddBotToGroup(bot, chat); }), + }), + Ui::LayerOption::KeepOther); + } +} + +auto AddBotToGroupBoxController::createRow(not_null history) +-> std::unique_ptr { + if (!needToCreateRow(history->peer)) { + return nullptr; + } + return std::make_unique(history); +} + +bool AddBotToGroupBoxController::needToCreateRow( + not_null peer) const { + if (sharingBotGame()) { + if (!peer->canWrite() + || peer->amRestricted(ChatRestriction::SendGames)) { + return false; + } + return true; + } + if (const auto chat = peer->asChat()) { + if (_adminToGroup && chat->canAddAdmins()) { + _groups.fire_copy(peer); + } else { + return chat->canAddMembers(); + } + } else if (const auto group = peer->asMegagroup()) { + if (_adminToGroup && group->canAddAdmins()) { + _groups.fire_copy(peer); + } else { + return group->canAddMembers(); + } + } else if (const auto channel = peer->asBroadcast()) { + if (_adminToChannel && channel->canAddAdmins()) { + _channels.fire_copy(peer); + } + } + return false; +} + +bool AddBotToGroupBoxController::SharingBotGame(not_null bot) { + const auto &info = bot->botInfo; + return (info && !info->shareGameShortName.isEmpty()); +} + +bool AddBotToGroupBoxController::sharingBotGame() const { + return SharingBotGame(_bot); +} + +QString AddBotToGroupBoxController::emptyBoxText() const { + return !session().data().chatsListLoaded() + ? tr::lng_contacts_loading(tr::now) + : (sharingBotGame() || _adminToChannel) + ? tr::lng_bot_no_chats(tr::now) + : tr::lng_bot_no_groups(tr::now); +} + +QString AddBotToGroupBoxController::noResultsText() const { + return !session().data().chatsListLoaded() + ? tr::lng_contacts_loading(tr::now) + : (sharingBotGame() || _adminToChannel) + ? tr::lng_bot_chats_not_found(tr::now) + : tr::lng_bot_groups_not_found(tr::now); +} + +void AddBotToGroupBoxController::updateLabels() { + setSearchNoResultsText(noResultsText()); +} + +object_ptr AddBotToGroupBoxController::prepareAdminnedChats() { + auto result = object_ptr((QWidget*)nullptr); + const auto container = result.data(); + + const auto callback = [=](not_null chat) { + addBotToGroup(chat); + }; + + const auto addList = [&]( + tr::phrase<> subtitle, + rpl::event_stream> &items) { + container->add(CreatePeerListSectionSubtitle( + container, + subtitle())); + container->add(object_ptr( + container, + st::membersMarginTop)); + + const auto delegate = container->lifetime().make_state< + PeerListContentDelegateSimple + >(); + const auto controller = container->lifetime().make_state( + &session(), + items.events(), + callback); + const auto content = result->add(object_ptr( + container, + controller)); + delegate->setContent(content); + controller->setDelegate(delegate); + + container->add(object_ptr( + container, + st::membersMarginBottom)); + }; + if (_adminToChannel) { + addList(tr::lng_bot_channels_manage, _channels); + } + if (_adminToGroup) { + addList(tr::lng_bot_groups_manage, _groups); + } + + rpl::merge( + _groups.events(), + _channels.events() + ) | rpl::take(1) | rpl::start_with_next([=] { + container->add(CreatePeerListSectionSubtitle( + container, + tr::lng_bot_groups())); + }, container->lifetime()); + + return result; +} + +void AddBotToGroupBoxController::prepareViewHook() { + delegate()->peerListSetTitle((sharingBotGame() || _adminToChannel) + ? tr::lng_bot_choose_chat() + : tr::lng_bot_choose_group()); + if (_adminToGroup || _adminToChannel) { + delegate()->peerListSetAboveWidget(prepareAdminnedChats()); + } + + updateLabels(); + session().data().chatsListLoadedEvents( + ) | rpl::filter([=](Data::Folder *folder) { + return !folder; + }) | rpl::start_with_next([=] { + updateLabels(); + }, lifetime()); +} + +void AddBotToGroup(not_null bot, not_null chat) { + if (bot->isBot() && !bot->botInfo->startGroupToken.isEmpty()) { + chat->session().api().sendBotStart(bot, chat); + } else { + chat->session().api().chatParticipants().add(chat, { 1, bot }); + } + Ui::hideLayer(); + Ui::showPeerHistory(chat, ShowAtUnreadMsgId); +} diff --git a/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.h b/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.h new file mode 100644 index 0000000000..4a2b3ae40d --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/add_bot_to_chat_box.h @@ -0,0 +1,49 @@ +/* +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 + +#include "boxes/peer_list_controllers.h" + +class AddBotToGroupBoxController + : public ChatsListBoxController + , public base::has_weak_ptr { +public: + static void Start(not_null bot); + + explicit AddBotToGroupBoxController(not_null bot); + + Main::Session &session() const override; + void rowClicked(not_null row) override; + +protected: + std::unique_ptr createRow(not_null history) override; + void prepareViewHook() override; + QString emptyBoxText() const override; + +private: + static bool SharingBotGame(not_null bot); + + object_ptr prepareAdminnedChats(); + + bool needToCreateRow(not_null peer) const; + bool sharingBotGame() const; + QString noResultsText() const; + void updateLabels(); + + void shareBotGame(not_null chat); + void addBotToGroup(not_null chat); + + const not_null _bot; + rpl::event_stream> _groups; + rpl::event_stream> _channels; + bool _adminToGroup = false; + bool _adminToChannel = false; + +}; + +void AddBotToGroup(not_null bot, not_null chat); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index 20598ee306..9134a1d49e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_privacy_security.h" #include "ui/boxes/confirm_box.h" #include "boxes/passcode_box.h" +#include "boxes/peers/add_bot_to_chat_box.h" #include "boxes/peers/edit_peer_permissions_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "data/data_peer_values.h" @@ -198,7 +199,8 @@ EditAdminBox::EditAdminBox( not_null peer, not_null user, ChatAdminRightsInfo rights, - const QString &rank) + const QString &rank, + bool addingBot) : EditParticipantBox( nullptr, peer, @@ -206,7 +208,8 @@ EditAdminBox::EditAdminBox( (rights.flags != 0)) , _show(this) , _oldRights(rights) -, _oldRank(rank) { +, _oldRank(rank) +, _addingBot(addingBot) { } ChatAdminRightsInfo EditAdminBox::defaultRights() const { @@ -236,12 +239,40 @@ void EditAdminBox::prepare() { EditParticipantBox::prepare(); - setTitle(_oldRights.flags + setTitle(_addingBot + ? tr::lng_bot_add_title() + : _oldRights.flags ? tr::lng_rights_edit_admin() : tr::lng_channel_add_admin()); - addControl( - object_ptr(this), + if (_addingBot && !peer()->isBroadcast() && _saveCallback) { + addControl( + object_ptr(this), + st::rightsDividerMargin / 2); + _addAsAdmin = addControl( + object_ptr( + this, + tr::lng_bot_as_admin_check(tr::now), + st::rightsCheckbox, + std::make_unique( + st::rightsToggle, + true)), + st::rightsToggleMargin + (st::rightsDividerMargin / 2)); + _addAsAdmin->checkedChanges( + ) | rpl::start_with_next([=](bool checked) { + _adminControlsWrap->toggle(checked, anim::type::normal); + refreshButtons(); + }, _addAsAdmin->lifetime()); + } + + _adminControlsWrap = addControl( + object_ptr>( + this, + object_ptr(this))); + const auto inner = _adminControlsWrap->entity(); + + inner->add( + object_ptr(inner), st::rightsDividerMargin); const auto chat = peer()->asChat(); @@ -290,21 +321,21 @@ void EditAdminBox::prepare() { ? chat->anyoneCanAddMembers() : channel->anyoneCanAddMembers(); auto [checkboxes, getChecked, changes] = CreateEditAdminRights( - this, + inner, tr::lng_rights_edit_admin_header(), prepareFlags, disabledMessages, isGroup, anyoneCanAddMembers); - addControl(std::move(checkboxes), QMargins()); + inner->add(std::move(checkboxes), QMargins()); auto selectedFlags = rpl::single( getChecked() ) | rpl::then(std::move( changes )); - _aboutAddAdmins = addControl( - object_ptr(this, st::boxDividerLabel), + _aboutAddAdmins = inner->add( + object_ptr(inner, st::boxDividerLabel), st::rightsAboutMargin); rpl::duplicate( selectedFlags @@ -318,6 +349,7 @@ void EditAdminBox::prepare() { if (canTransferOwnership()) { const auto allFlags = AdminRightsForOwnershipTransfer(isGroup); setupTransferButton( + inner, isGroup )->toggleOn(rpl::duplicate( selectedFlags @@ -327,37 +359,76 @@ void EditAdminBox::prepare() { } if (canSave()) { - const auto rank = (chat || channel->isMegagroup()) - ? addRankInput().get() + _rank = (chat || channel->isMegagroup()) + ? addRankInput(inner).get() : nullptr; - - addButton(tr::lng_settings_save(), [=, value = getChecked] { - if (!_saveCallback) { - return; - } + _finishSave = [=, value = getChecked] { const auto newFlags = (value() | ChatAdminRight::Other) & ((!channel || channel->amCreator()) ? ~Flags(0) : channel->adminRights()); _saveCallback( - _oldRights, + _addingBot ? ChatAdminRightsInfo() : _oldRights, ChatAdminRightsInfo(newFlags), - rank ? rank->getLastText().trimmed() : QString()); - }); + _rank ? _rank->getLastText().trimmed() : QString()); + }; + _save = [=] { + if (!_saveCallback) { + return; + } else if (_addAsAdmin && !_addAsAdmin->checked()) { + AddBotToGroup(user(), peer()); + return; + } else if (_addingBot) { + const auto phrase = peer()->isBroadcast() + ? tr::lng_bot_sure_add_text_channel + : tr::lng_bot_sure_add_text_group; + _confirmBox = getDelegate()->show(Ui::MakeConfirmBox({ + phrase( + tr::now, + lt_group, + Ui::Text::Bold(peer()->name), + Ui::Text::WithEntities), + crl::guard(this, [=] { finishAddAdmin(); }) + }), Ui::LayerOption::KeepOther); + } else { + _finishSave(); + } + }; + } + + refreshButtons(); +} + +void EditAdminBox::finishAddAdmin() { + _finishSave(); + if (_confirmBox) { + _confirmBox->closeBox(); + } +} + +void EditAdminBox::refreshButtons() { + clearButtons(); + if (canSave()) { + addButton(!_addingBot + ? tr::lng_settings_save() + : _adminControlsWrap->toggled() + ? tr::lng_bot_add_as_admin() + : tr::lng_bot_add_as_member(), _save); addButton(tr::lng_cancel(), [=] { closeBox(); }); } else { addButton(tr::lng_box_ok(), [=] { closeBox(); }); } } -not_null EditAdminBox::addRankInput() { - addControl( - object_ptr(this), +not_null EditAdminBox::addRankInput( + not_null container) { + container->add( + object_ptr(container), st::rightsRankMargin); - addControl( + container->add( object_ptr( - this, + container, tr::lng_rights_edit_admin_rank_name(), st::rightsHeaderLabel), st::rightsHeaderMargin); @@ -372,9 +443,9 @@ not_null EditAdminBox::addRankInput() { } Unexpected("Peer type in EditAdminBox::addRankInput."); }(); - const auto result = addControl( + const auto result = container->add( object_ptr( - this, + container, st::customBadgeField, (isOwner ? tr::lng_owner_badge : tr::lng_admin_badge)(), TextUtilities::RemoveEmoji(_oldRank)), @@ -389,9 +460,9 @@ not_null EditAdminBox::addRankInput() { } }); - addControl( + container->add( object_ptr( - this, + container, tr::lng_rights_edit_admin_rank_about( lt_title, (isOwner ? tr::lng_owner_badge : tr::lng_admin_badge)()), @@ -413,19 +484,20 @@ bool EditAdminBox::canTransferOwnership() const { } not_null*> EditAdminBox::setupTransferButton( + not_null container, bool isGroup) { - const auto wrap = addControl( + const auto wrap = container->add( object_ptr>( - this, - object_ptr(this))); + container, + object_ptr(container))); - const auto container = wrap->entity(); + const auto inner = wrap->entity(); - container->add( - object_ptr(container), + inner->add( + object_ptr(inner), { 0, st::infoProfileSkip, 0, st::infoProfileSkip }); - container->add(EditPeerInfoBox::CreateButton( - this, + inner->add(EditPeerInfoBox::CreateButton( + inner, (isGroup ? tr::lng_rights_transfer_group : tr::lng_rights_transfer_channel)(), diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index 61c89a377d..a7cf34121c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -18,6 +18,7 @@ class Checkbox; class Radiobutton; class RadiobuttonGroup; class CalendarBox; +class VerticalLayout; template class SlideWrap; } // namespace Ui @@ -71,7 +72,8 @@ public: not_null peer, not_null user, ChatAdminRightsInfo rights, - const QString &rank); + const QString &rank, + bool addingBot = false); void setSaveCallback( Fn addRankInput(); + not_null addRankInput( + not_null container); void transferOwnership(); void transferOwnershipChecked(); bool handleTransferPasswordError(const QString &error); @@ -99,9 +102,13 @@ private: bool canSave() const { return _saveCallback != nullptr; } + void finishAddAdmin(); + void refreshButtons(); void refreshAboutAddAdminsText(bool canAddAdmins); bool canTransferOwnership() const; - not_null*> setupTransferButton(bool isGroup); + not_null*> setupTransferButton( + not_null container, + bool isGroup); const Ui::BoxShow _show; const ChatAdminRightsInfo _oldRights; @@ -111,9 +118,16 @@ private: ChatAdminRightsInfo, const QString &rank)> _saveCallback; + QPointer _confirmBox; + Ui::Checkbox *_addAsAdmin = nullptr; + Ui::SlideWrap *_adminControlsWrap = nullptr; + Ui::InputField *_rank = nullptr; QPointer _aboutAddAdmins; mtpRequestId _checkTransferRequestId = 0; mtpRequestId _transferRequestId = 0; + Fn _save, _finishSave; + + bool _addingBot = false; }; diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index f46731e2f8..17be7979c3 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/add_contact_box.h" +#include "boxes/peers/add_bot_to_chat_box.h" #include "boxes/peers/edit_contact_box.h" #include "lang/lang_keys.h" #include "info/info_controller.h" diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index c1ad241042..1f8d9b71f9 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/add_contact_box.h" #include "boxes/create_poll_box.h" #include "boxes/pin_messages_box.h" +#include "boxes/peers/add_bot_to_chat_box.h" #include "boxes/peers/add_participants_box.h" #include "boxes/peers/edit_contact_box.h" #include "ui/boxes/report_box.h" diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index be279af45b..264f91b604 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "boxes/add_contact_box.h" +#include "boxes/peers/add_bot_to_chat_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/delete_messages_box.h"