558 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			558 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 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/choose_peer_box.h"
 | |
| 
 | |
| #include "apiwrap.h" // ApiWrap::botCommonGroups / requestBotCommonGroups.
 | |
| #include "boxes/add_contact_box.h"
 | |
| #include "boxes/peer_list_controllers.h"
 | |
| #include "boxes/premium_limits_box.h"
 | |
| #include "data/data_chat.h"
 | |
| #include "data/data_channel.h"
 | |
| #include "data/data_peer.h"
 | |
| #include "data/data_user.h"
 | |
| #include "history/history.h"
 | |
| #include "history/history_item_reply_markup.h"
 | |
| #include "info/profile/info_profile_icon.h"
 | |
| #include "lang/lang_keys.h"
 | |
| #include "main/main_session.h" // Session::api().
 | |
| #include "ui/boxes/confirm_box.h"
 | |
| #include "ui/text/text_utilities.h"
 | |
| #include "ui/widgets/buttons.h"
 | |
| #include "ui/wrap/vertical_layout.h"
 | |
| #include "ui/vertical_list.h"
 | |
| #include "window/window_session_controller.h"
 | |
| #include "styles/style_boxes.h"
 | |
| #include "styles/style_chat_helpers.h"
 | |
| #include "styles/style_layers.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class ChoosePeerBoxController final
 | |
| 	: public ChatsListBoxController
 | |
| 	, public base::has_weak_ptr {
 | |
| public:
 | |
| 	ChoosePeerBoxController(
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<UserData*> bot,
 | |
| 		RequestPeerQuery query,
 | |
| 		Fn<void(std::vector<not_null<PeerData*>>)> callback);
 | |
| 
 | |
| 	Main::Session &session() const override;
 | |
| 	void rowClicked(not_null<PeerListRow*> row) override;
 | |
| 
 | |
| 	[[nodiscard]] rpl::producer<int> selectedCountValue() const;
 | |
| 	void submit();
 | |
| 
 | |
| 	QString savedMessagesChatStatus() const override {
 | |
| 		return {};
 | |
| 	}
 | |
| 
 | |
| private:
 | |
| 	void prepareViewHook() override;
 | |
| 	std::unique_ptr<Row> createRow(not_null<History*> history) override;
 | |
| 	QString emptyBoxText() const override;
 | |
| 
 | |
| 	void prepareRestrictions();
 | |
| 
 | |
| 	const not_null<Window::SessionNavigation*> _navigation;
 | |
| 	not_null<UserData*> _bot;
 | |
| 	RequestPeerQuery _query;
 | |
| 	base::flat_set<not_null<PeerData*>> _commonGroups;
 | |
| 	base::flat_set<not_null<PeerData*>> _selected;
 | |
| 	rpl::variable<int> _selectedCount;
 | |
| 	Fn<void(std::vector<not_null<PeerData*>>)> _callback;
 | |
| 
 | |
| };
 | |
| 
 | |
| using RightsMap = std::vector<std::pair<ChatAdminRight, tr::phrase<>>>;
 | |
| 
 | |
| [[nodiscard]] RightsMap GroupRights() {
 | |
| 	using Flag = ChatAdminRight;
 | |
| 	return {
 | |
| 		{ Flag::ChangeInfo, tr::lng_request_group_change_info },
 | |
| 		{
 | |
| 			Flag::DeleteMessages,
 | |
| 			tr::lng_request_group_delete_messages },
 | |
| 		{ Flag::BanUsers, tr::lng_request_group_ban_users },
 | |
| 		{ Flag::InviteByLinkOrAdd, tr::lng_request_group_invite },
 | |
| 		{ Flag::PinMessages, tr::lng_request_group_pin_messages },
 | |
| 		{ Flag::ManageTopics, tr::lng_request_group_manage_topics },
 | |
| 		{
 | |
| 			Flag::ManageCall,
 | |
| 			tr::lng_request_group_manage_video_chats },
 | |
| 		{ Flag::Anonymous, tr::lng_request_group_anonymous },
 | |
| 		{ Flag::AddAdmins, tr::lng_request_group_add_admins },
 | |
| 	};
 | |
| }
 | |
| 
 | |
| [[nodiscard]] RightsMap BroadcastRights() {
 | |
| 	using Flag = ChatAdminRight;
 | |
| 	return {
 | |
| 		{ Flag::ChangeInfo, tr::lng_request_channel_change_info },
 | |
| 		{
 | |
| 			Flag::PostMessages,
 | |
| 			tr::lng_request_channel_post_messages },
 | |
| 		{
 | |
| 			Flag::EditMessages,
 | |
| 			tr::lng_request_channel_edit_messages },
 | |
| 		{
 | |
| 			Flag::DeleteMessages,
 | |
| 			tr::lng_request_channel_delete_messages },
 | |
| 		{
 | |
| 			Flag::InviteByLinkOrAdd,
 | |
| 			tr::lng_request_channel_add_subscribers },
 | |
| 		{
 | |
| 			Flag::ManageCall,
 | |
| 			tr::lng_request_channel_manage_livestreams },
 | |
| 		{ Flag::AddAdmins, tr::lng_request_channel_add_admins },
 | |
| 	};
 | |
| }
 | |
| 
 | |
| [[nodiscard]] QString RightsText(
 | |
| 		ChatAdminRights rights,
 | |
| 		const RightsMap &phrases) {
 | |
| 	auto list = QStringList();
 | |
| 	for (const auto &[flag, phrase] : phrases) {
 | |
| 		if (rights & flag) {
 | |
| 			list.push_back(phrase(tr::now));
 | |
| 		}
 | |
| 	}
 | |
| 	const auto count = list.size();
 | |
| 	if (!count) {
 | |
| 		return QString();
 | |
| 	}
 | |
| 	const auto last = list.back();
 | |
| 	return (count > 1)
 | |
| 		? tr::lng_request_peer_rights_and(
 | |
| 			tr::now,
 | |
| 			lt_rights,
 | |
| 			list.mid(0, count - 1).join(", "),
 | |
| 			lt_last,
 | |
| 			last)
 | |
| 		: last;
 | |
| }
 | |
| 
 | |
| [[nodiscard]] QString GroupRightsText(ChatAdminRights rights) {
 | |
| 	return RightsText(rights, GroupRights());
 | |
| }
 | |
| 
 | |
| [[nodiscard]] QString BroadcastRightsText(ChatAdminRights rights) {
 | |
| 	return RightsText(rights, BroadcastRights());
 | |
| }
 | |
| 
 | |
| [[nodiscard]] QStringList RestrictionsList(RequestPeerQuery query) {
 | |
| 	using Type = RequestPeerQuery::Type;
 | |
| 	using Restriction = RequestPeerQuery::Restriction;
 | |
| 	auto result = QStringList();
 | |
| 	const auto addRestriction = [&](
 | |
| 			Restriction value,
 | |
| 			tr::phrase<> yes,
 | |
| 			tr::phrase<> no) {
 | |
| 		if (value == Restriction::Yes) {
 | |
| 			result.push_back(yes(tr::now));
 | |
| 		} else if (value == Restriction::No) {
 | |
| 			result.push_back(no(tr::now));
 | |
| 		}
 | |
| 	};
 | |
| 	const auto addRights = [&](const QString &rights) {
 | |
| 		if (!rights.isEmpty()) {
 | |
| 			result.push_back(
 | |
| 				tr::lng_request_peer_rights(tr::now, lt_rights, rights));
 | |
| 		}
 | |
| 	};
 | |
| 	switch (query.type) {
 | |
| 	case Type::User:
 | |
| 		if (query.userIsBot != Restriction::Yes) {
 | |
| 			addRestriction(
 | |
| 				query.userIsPremium,
 | |
| 				tr::lng_request_user_premium_yes,
 | |
| 				tr::lng_request_user_premium_no);
 | |
| 		}
 | |
| 		break;
 | |
| 	case Type::Group:
 | |
| 		addRestriction(
 | |
| 			query.hasUsername,
 | |
| 			tr::lng_request_group_public_yes,
 | |
| 			tr::lng_request_group_public_no);
 | |
| 		addRestriction(
 | |
| 			query.groupIsForum,
 | |
| 			tr::lng_request_group_topics_yes,
 | |
| 			tr::lng_request_group_topics_no);
 | |
| 		if (query.amCreator) {
 | |
| 			result.push_back(tr::lng_request_group_am_owner(tr::now));
 | |
| 		} else {
 | |
| 			addRights(GroupRightsText(query.myRights));
 | |
| 		}
 | |
| 		break;
 | |
| 	case Type::Broadcast:
 | |
| 		addRestriction(
 | |
| 			query.hasUsername,
 | |
| 			tr::lng_request_channel_public_yes,
 | |
| 			tr::lng_request_channel_public_no);
 | |
| 		if (query.amCreator) {
 | |
| 			result.push_back(tr::lng_request_channel_am_owner(tr::now));
 | |
| 		} else {
 | |
| 			addRights(BroadcastRightsText(query.myRights));
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::BoxContent> MakeConfirmBox(
 | |
| 		not_null<UserData*> bot,
 | |
| 		not_null<PeerData*> peer,
 | |
| 		RequestPeerQuery query,
 | |
| 		Fn<void()> confirmed) {
 | |
| 	const auto name = peer->name();
 | |
| 	const auto botName = bot->name();
 | |
| 	auto text = tr::lng_request_peer_confirm(
 | |
| 		tr::now,
 | |
| 		lt_chat,
 | |
| 		Ui::Text::Bold(name),
 | |
| 		lt_bot,
 | |
| 		Ui::Text::Bold(botName),
 | |
| 		Ui::Text::WithEntities);
 | |
| 	if (!peer->isUser()) {
 | |
| 		const auto rights = peer->isBroadcast()
 | |
| 			? BroadcastRightsText(query.botRights)
 | |
| 			: GroupRightsText(query.botRights);
 | |
| 		if (!rights.isEmpty()) {
 | |
| 			text.append('\n').append('\n').append(
 | |
| 				tr::lng_request_peer_confirm_rights(
 | |
| 					tr::now,
 | |
| 					lt_bot,
 | |
| 					Ui::Text::Bold(botName),
 | |
| 					lt_chat,
 | |
| 					Ui::Text::Bold(name),
 | |
| 					lt_rights,
 | |
| 					TextWithEntities{ rights },
 | |
| 					Ui::Text::WithEntities));
 | |
| 		} else if (!peer->isBroadcast() && query.isBotParticipant) {
 | |
| 			const auto common = bot->session().api().botCommonGroups(bot);
 | |
| 			if (!common || !ranges::contains(*common, peer)) {
 | |
| 				text.append('\n').append('\n').append(
 | |
| 					tr::lng_request_peer_confirm_add(
 | |
| 						tr::now,
 | |
| 						lt_bot,
 | |
| 						Ui::Text::Bold(botName),
 | |
| 						lt_chat,
 | |
| 						Ui::Text::Bold(name),
 | |
| 						Ui::Text::WithEntities));
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return Ui::MakeConfirmBox({
 | |
| 		.text = std::move(text),
 | |
| 		.confirmed = [=](Fn<void()> close) { confirmed(); close(); },
 | |
| 		.confirmText = tr::lng_request_peer_confirm_send(tr::now),
 | |
| 	});
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::BoxContent> CreatePeerByQueryBox(
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<UserData*> bot,
 | |
| 		RequestPeerQuery query,
 | |
| 		Fn<void(std::vector<not_null<PeerData*>>)> done) {
 | |
| 	const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
 | |
| 	auto callback = [=](not_null<PeerData*> peer) {
 | |
| 		done({ peer });
 | |
| 		if (const auto strong = weak->data()) {
 | |
| 			strong->closeBox();
 | |
| 		}
 | |
| 	};
 | |
| 	auto result = Box<GroupInfoBox>(
 | |
| 		navigation,
 | |
| 		bot,
 | |
| 		query,
 | |
| 		std::move(callback));
 | |
| 	*weak = result.data();
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| [[nodiscard]] bool FilterPeerByQuery(
 | |
| 		not_null<PeerData*> peer,
 | |
| 		RequestPeerQuery query,
 | |
| 		const base::flat_set<not_null<PeerData*>> &commonGroups) {
 | |
| 	using Type = RequestPeerQuery::Type;
 | |
| 	using Restriction = RequestPeerQuery::Restriction;
 | |
| 	const auto checkRestriction = [](Restriction restriction, bool value) {
 | |
| 		return (restriction == Restriction::Any)
 | |
| 			|| ((restriction == Restriction::Yes) == value);
 | |
| 	};
 | |
| 	const auto checkRights = [](
 | |
| 			ChatAdminRights wanted,
 | |
| 			bool creator,
 | |
| 			ChatAdminRights rights) {
 | |
| 		return creator || ((rights & wanted) == wanted);
 | |
| 	};
 | |
| 	switch (query.type) {
 | |
| 	case Type::User: {
 | |
| 		const auto user = peer->asUser();
 | |
| 		return user
 | |
| 			&& !user->isInaccessible()
 | |
| 			&& !user->isNotificationsUser()
 | |
| 			&& checkRestriction(query.userIsBot, user->isBot())
 | |
| 			&& checkRestriction(query.userIsPremium, user->isPremium());
 | |
| 	}
 | |
| 	case Type::Group: {
 | |
| 		const auto chat = peer->asChat();
 | |
| 		const auto megagroup = peer->asMegagroup();
 | |
| 		return (chat || megagroup)
 | |
| 			&& (!query.amCreator
 | |
| 				|| (chat ? chat->amCreator() : megagroup->amCreator()))
 | |
| 			&& checkRestriction(query.groupIsForum, peer->isForum())
 | |
| 			&& checkRestriction(
 | |
| 				query.hasUsername,
 | |
| 				megagroup && megagroup->hasUsername())
 | |
| 			&& checkRights(
 | |
| 				query.myRights,
 | |
| 				chat ? chat->amCreator() : megagroup->amCreator(),
 | |
| 				chat ? chat->adminRights() : megagroup->adminRights())
 | |
| 			&& (!query.isBotParticipant
 | |
| 				|| query.myRights
 | |
| 				|| commonGroups.contains(peer)
 | |
| 				|| (chat
 | |
| 					? chat->canAddMembers()
 | |
| 					: megagroup->canAddMembers()));
 | |
| 	}
 | |
| 	case Type::Broadcast: {
 | |
| 		const auto broadcast = peer->asBroadcast();
 | |
| 		return broadcast
 | |
| 			&& (!query.amCreator || broadcast->amCreator())
 | |
| 			&& checkRestriction(query.hasUsername, broadcast->hasUsername())
 | |
| 			&& checkRights(
 | |
| 				query.myRights,
 | |
| 				broadcast->amCreator(),
 | |
| 				broadcast->adminRights());
 | |
| 	}
 | |
| 	}
 | |
| 	Unexpected("Type in FilterPeerByQuery.");
 | |
| }
 | |
| 
 | |
| ChoosePeerBoxController::ChoosePeerBoxController(
 | |
| 	not_null<Window::SessionNavigation*> navigation,
 | |
| 	not_null<UserData*> bot,
 | |
| 	RequestPeerQuery query,
 | |
| 	Fn<void(std::vector<not_null<PeerData*>>)> callback)
 | |
| : ChatsListBoxController(&navigation->session())
 | |
| , _navigation(navigation)
 | |
| , _bot(bot)
 | |
| , _query(query)
 | |
| , _callback(std::move(callback)) {
 | |
| 	if (const auto list = _bot->session().api().botCommonGroups(_bot)) {
 | |
| 		_commonGroups = { begin(*list), end(*list) };
 | |
| 	}
 | |
| }
 | |
| 
 | |
| Main::Session &ChoosePeerBoxController::session() const {
 | |
| 	return _navigation->session();
 | |
| }
 | |
| 
 | |
| void ChoosePeerBoxController::prepareRestrictions() {
 | |
| 	auto above = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
 | |
| 	const auto raw = above.data();
 | |
| 	auto rows = RestrictionsList(_query);
 | |
| 	if (!rows.empty()) {
 | |
| 		Ui::AddSubsectionTitle(
 | |
| 			raw,
 | |
| 			tr::lng_request_peer_requirements(),
 | |
| 			{ 0, st::membersMarginTop, 0, 0 });
 | |
| 		const auto skip = st::defaultSubsectionTitlePadding.left();
 | |
| 		auto separator = QString::fromUtf8("\n\xE2\x80\xA2 ");
 | |
| 		raw->add(
 | |
| 			object_ptr<Ui::FlatLabel>(
 | |
| 				raw,
 | |
| 				separator + rows.join(separator),
 | |
| 				st::requestPeerRestriction),
 | |
| 			{ skip, 0, skip, st::membersMarginTop });
 | |
| 		Ui::AddDivider(raw);
 | |
| 	}
 | |
| 	const auto make = [&](tr::phrase<> text, const style::icon &st) {
 | |
| 		auto button = raw->add(
 | |
| 			object_ptr<Ui::SettingsButton>(
 | |
| 				raw,
 | |
| 				text(),
 | |
| 				st::inviteViaLinkButton),
 | |
| 			{ 0, st::membersMarginTop, 0, 0 });
 | |
| 		const auto icon = Ui::CreateChild<Info::Profile::FloatingIcon>(
 | |
| 			button,
 | |
| 			st,
 | |
| 			QPoint());
 | |
| 		button->heightValue(
 | |
| 		) | rpl::start_with_next([=](int height) {
 | |
| 			icon->moveToLeft(
 | |
| 				st::choosePeerCreateIconLeft,
 | |
| 				(height - st::inviteViaLinkIcon.height()) / 2);
 | |
| 		}, icon->lifetime());
 | |
| 
 | |
| 		button->setClickedCallback([=] {
 | |
| 			_navigation->parentController()->show(
 | |
| 				CreatePeerByQueryBox(_navigation, _bot, _query, _callback));
 | |
| 		});
 | |
| 
 | |
| 		button->events(
 | |
| 		) | rpl::filter([=](not_null<QEvent*> e) {
 | |
| 			return (e->type() == QEvent::Enter);
 | |
| 		}) | rpl::start_with_next([=] {
 | |
| 			delegate()->peerListMouseLeftGeometry();
 | |
| 		}, button->lifetime());
 | |
| 		return button;
 | |
| 	};
 | |
| 	if (_query.type == RequestPeerQuery::Type::Group) {
 | |
| 		make(tr::lng_request_group_create, st::choosePeerGroupIcon);
 | |
| 	} else if (_query.type == RequestPeerQuery::Type::Broadcast) {
 | |
| 		make(tr::lng_request_channel_create, st::choosePeerChannelIcon);
 | |
| 	}
 | |
| 
 | |
| 	if (raw->count() > 0) {
 | |
| 		delegate()->peerListSetAboveWidget(std::move(above));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ChoosePeerBoxController::prepareViewHook() {
 | |
| 	delegate()->peerListSetTitle([&] {
 | |
| 		using Type = RequestPeerQuery::Type;
 | |
| 		using Restriction = RequestPeerQuery::Restriction;
 | |
| 		switch (_query.type) {
 | |
| 		case Type::User: return (_query.userIsBot == Restriction::Yes)
 | |
| 			? tr::lng_request_bot_title()
 | |
| 			: (_query.maxQuantity > 1)
 | |
| 			? tr::lng_request_users_title()
 | |
| 			: tr::lng_request_user_title();
 | |
| 		case Type::Group: return tr::lng_request_group_title();
 | |
| 		case Type::Broadcast: return tr::lng_request_channel_title();
 | |
| 		}
 | |
| 		Unexpected("Type in RequestPeerQuery.");
 | |
| 	}());
 | |
| 	prepareRestrictions();
 | |
| }
 | |
| 
 | |
| void ChoosePeerBoxController::rowClicked(not_null<PeerListRow*> row) {
 | |
| 	const auto limit = _query.maxQuantity;
 | |
| 	const auto multiselect = (limit > 1);
 | |
| 	const auto peer = row->peer();
 | |
| 	if (multiselect) {
 | |
| 		if (_selected.contains(peer) || _selected.size() < limit) {
 | |
| 			delegate()->peerListSetRowChecked(row, !row->checked());
 | |
| 			if (row->checked()) {
 | |
| 				_selected.emplace(peer);
 | |
| 			} else {
 | |
| 				_selected.remove(peer);
 | |
| 			}
 | |
| 			_selectedCount = int(_selected.size());
 | |
| 		}
 | |
| 		return;
 | |
| 	}
 | |
| 	const auto done = [callback = _callback, peer] {
 | |
| 		const auto onstack = callback;
 | |
| 		onstack({ peer });
 | |
| 	};
 | |
| 	if (const auto user = peer->asUser()) {
 | |
| 		done();
 | |
| 	} else {
 | |
| 		delegate()->peerListUiShow()->showBox(
 | |
| 			MakeConfirmBox(_bot, peer, _query, done));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| rpl::producer<int> ChoosePeerBoxController::selectedCountValue() const {
 | |
| 	return _selectedCount.value();
 | |
| }
 | |
| 
 | |
| void ChoosePeerBoxController::submit() {
 | |
| 	const auto onstack = _callback;
 | |
| 	onstack(ranges::to_vector(_selected));
 | |
| }
 | |
| 
 | |
| auto ChoosePeerBoxController::createRow(not_null<History*> history)
 | |
| -> std::unique_ptr<Row> {
 | |
| 	return FilterPeerByQuery(history->peer, _query, _commonGroups)
 | |
| 		? std::make_unique<Row>(history)
 | |
| 		: nullptr;
 | |
| }
 | |
| 
 | |
| QString ChoosePeerBoxController::emptyBoxText() const {
 | |
| 	using Type = RequestPeerQuery::Type;
 | |
| 	using Restriction = RequestPeerQuery::Restriction;
 | |
| 
 | |
| 	const auto result = [](tr::phrase<> title, tr::phrase<> text) {
 | |
| 		return title(tr::now) + "\n\n" + text(tr::now);
 | |
| 	};
 | |
| 	switch (_query.type) {
 | |
| 	case Type::User: return (_query.userIsBot == Restriction::Yes)
 | |
| 		? result(tr::lng_request_bot_no, tr::lng_request_bot_no_about)
 | |
| 		: result(tr::lng_request_user_no, tr::lng_request_user_no_about);
 | |
| 	case Type::Group:
 | |
| 		return result(
 | |
| 			tr::lng_request_group_no,
 | |
| 			tr::lng_request_group_no_about);
 | |
| 	case Type::Broadcast:
 | |
| 		return result(
 | |
| 			tr::lng_request_channel_no,
 | |
| 			tr::lng_request_channel_no_about);
 | |
| 	}
 | |
| 	Unexpected("Type in ChoosePeerBoxController::emptyBoxText.");
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| void ShowChoosePeerBox(
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<UserData*> bot,
 | |
| 		RequestPeerQuery query,
 | |
| 		Fn<void(std::vector<not_null<PeerData*>>)> chosen) {
 | |
| 	const auto needCommonGroups = query.isBotParticipant
 | |
| 		&& (query.type == RequestPeerQuery::Type::Group)
 | |
| 		&& !query.myRights;
 | |
| 	if (needCommonGroups && !bot->session().api().botCommonGroups(bot)) {
 | |
| 		const auto weak = base::make_weak(navigation);
 | |
| 		bot->session().api().requestBotCommonGroups(bot, [=] {
 | |
| 			if (const auto strong = weak.get()) {
 | |
| 				ShowChoosePeerBox(strong, bot, query, chosen);
 | |
| 			}
 | |
| 		});
 | |
| 		return;
 | |
| 	}
 | |
| 	const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
 | |
| 	auto callback = [=, done = std::move(chosen)](
 | |
| 			std::vector<not_null<PeerData*>> peers) {
 | |
| 		done(std::move(peers));
 | |
| 		if (const auto strong = weak->data()) {
 | |
| 			strong->closeBox();
 | |
| 		}
 | |
| 	};
 | |
| 	const auto limit = query.maxQuantity;
 | |
| 	auto controller = std::make_unique<ChoosePeerBoxController>(
 | |
| 		navigation,
 | |
| 		bot,
 | |
| 		query,
 | |
| 		std::move(callback));
 | |
| 	auto initBox = [=, ptr = controller.get()](not_null<PeerListBox*> box) {
 | |
| 		ptr->selectedCountValue() | rpl::start_with_next([=](int count) {
 | |
| 			box->clearButtons();
 | |
| 			if (limit > 1) {
 | |
| 				box->setAdditionalTitle(rpl::single(u"%1 / %2"_q.arg(count).arg(limit)));
 | |
| 			}
 | |
| 			if (count > 0) {
 | |
| 				box->addButton(tr::lng_intro_submit(), [=] {
 | |
| 					ptr->submit();
 | |
| 					if (*weak) {
 | |
| 						(*weak)->closeBox();
 | |
| 					}
 | |
| 				});
 | |
| 			}
 | |
| 			box->addButton(tr::lng_cancel(), [box] {
 | |
| 				box->closeBox();
 | |
| 			});
 | |
| 		}, box->lifetime());
 | |
| 	};
 | |
| 	*weak = navigation->parentController()->show(Box<PeerListBox>(
 | |
| 		std::move(controller),
 | |
| 		std::move(initBox)));
 | |
| }
 | 
