384 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			384 lines
		
	
	
	
		
			10 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/edit_linked_chat_box.h"
 | |
| 
 | |
| #include "lang/lang_keys.h"
 | |
| #include "data/data_channel.h"
 | |
| #include "data/data_chat.h"
 | |
| #include "data/data_changes.h"
 | |
| #include "ui/widgets/labels.h"
 | |
| #include "ui/widgets/buttons.h"
 | |
| #include "ui/wrap/vertical_layout.h"
 | |
| #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
 | |
| #include "boxes/peer_list_box.h"
 | |
| #include "ui/boxes/confirm_box.h"
 | |
| #include "ui/toasts/common_toasts.h"
 | |
| #include "boxes/add_contact_box.h"
 | |
| #include "apiwrap.h"
 | |
| #include "main/main_session.h"
 | |
| #include "window/window_session_controller.h"
 | |
| #include "styles/style_layers.h"
 | |
| #include "styles/style_boxes.h"
 | |
| #include "styles/style_info.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| constexpr auto kEnableSearchRowsCount = 10;
 | |
| 
 | |
| class Controller : public PeerListController, public base::has_weak_ptr {
 | |
| public:
 | |
| 	Controller(
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<ChannelData*> channel,
 | |
| 		ChannelData *chat,
 | |
| 		const std::vector<not_null<PeerData*>> &chats,
 | |
| 		Fn<void(ChannelData*)> callback,
 | |
| 		Fn<void(not_null<PeerData*>)> showHistoryCallback);
 | |
| 
 | |
| 	Main::Session &session() const override;
 | |
| 	void prepare() override;
 | |
| 	void rowClicked(not_null<PeerListRow*> row) override;
 | |
| 	int contentWidth() const override;
 | |
| 
 | |
| private:
 | |
| 	void choose(not_null<ChannelData*> chat);
 | |
| 	void choose(not_null<ChatData*> chat);
 | |
| 
 | |
| 	not_null<Window::SessionNavigation*> _navigation;
 | |
| 	not_null<ChannelData*> _channel;
 | |
| 	ChannelData *_chat = nullptr;
 | |
| 	std::vector<not_null<PeerData*>> _chats;
 | |
| 	Fn<void(ChannelData*)> _callback;
 | |
| 	Fn<void(not_null<PeerData*>)> _showHistoryCallback;
 | |
| 
 | |
| 	ChannelData *_waitForFull = nullptr;
 | |
| 
 | |
| 	rpl::event_stream<not_null<PeerData*>> _showHistoryRequest;
 | |
| };
 | |
| 
 | |
| Controller::Controller(
 | |
| 	not_null<Window::SessionNavigation*> navigation,
 | |
| 	not_null<ChannelData*> channel,
 | |
| 	ChannelData *chat,
 | |
| 	const std::vector<not_null<PeerData*>> &chats,
 | |
| 	Fn<void(ChannelData*)> callback,
 | |
| 	Fn<void(not_null<PeerData*>)> showHistoryCallback)
 | |
| : _navigation(navigation)
 | |
| , _channel(channel)
 | |
| , _chat(chat)
 | |
| , _chats(std::move(chats))
 | |
| , _callback(std::move(callback))
 | |
| , _showHistoryCallback(std::move(showHistoryCallback)) {
 | |
| 	channel->session().changes().peerUpdates(
 | |
| 		Data::PeerUpdate::Flag::FullInfo
 | |
| 	) | rpl::filter([=](const Data::PeerUpdate &update) {
 | |
| 		return (update.peer == _waitForFull);
 | |
| 	}) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
 | |
| 		choose(std::exchange(_waitForFull, nullptr));
 | |
| 	}, lifetime());
 | |
| }
 | |
| 
 | |
| Main::Session &Controller::session() const {
 | |
| 	return _channel->session();
 | |
| }
 | |
| 
 | |
| int Controller::contentWidth() const {
 | |
| 	return st::boxWidth;
 | |
| }
 | |
| 
 | |
| void Controller::prepare() {
 | |
| 	const auto appendRow = [&](not_null<PeerData*> chat) {
 | |
| 		if (delegate()->peerListFindRow(chat->id.value)) {
 | |
| 			return;
 | |
| 		}
 | |
| 		auto row = std::make_unique<PeerListRow>(chat);
 | |
| 		const auto username = chat->userName();
 | |
| 		row->setCustomStatus(!username.isEmpty()
 | |
| 			? ('@' + username)
 | |
| 			: (chat->isChannel() && !chat->isMegagroup())
 | |
| 			? tr::lng_manage_linked_channel_private_status(tr::now)
 | |
| 			: tr::lng_manage_discussion_group_private_status(tr::now));
 | |
| 		delegate()->peerListAppendRow(std::move(row));
 | |
| 	};
 | |
| 	if (_chat) {
 | |
| 		appendRow(_chat);
 | |
| 	} else {
 | |
| 		for (const auto chat : _chats) {
 | |
| 			appendRow(chat);
 | |
| 		}
 | |
| 		if (_chats.size() >= kEnableSearchRowsCount) {
 | |
| 			delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Controller::rowClicked(not_null<PeerListRow*> row) {
 | |
| 	if (_chat != nullptr) {
 | |
| 		_showHistoryCallback(_chat);
 | |
| 		return;
 | |
| 	}
 | |
| 	const auto peer = row->peer();
 | |
| 	if (const auto channel = peer->asChannel()) {
 | |
| 		if (channel->wasFullUpdated()) {
 | |
| 			choose(channel);
 | |
| 			return;
 | |
| 		}
 | |
| 		_waitForFull = channel;
 | |
| 		channel->updateFull();
 | |
| 	} else if (const auto chat = peer->asChat()) {
 | |
| 		choose(chat);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Controller::choose(not_null<ChannelData*> chat) {
 | |
| 	if (chat->isForum()) {
 | |
| 		ShowForumForDiscussionError(_navigation);
 | |
| 		return;
 | |
| 	}
 | |
| 	auto text = tr::lng_manage_discussion_group_sure(
 | |
| 		tr::now,
 | |
| 		lt_group,
 | |
| 		Ui::Text::Bold(chat->name()),
 | |
| 		lt_channel,
 | |
| 		Ui::Text::Bold(_channel->name()),
 | |
| 		Ui::Text::WithEntities);
 | |
| 	if (!_channel->isPublic()) {
 | |
| 		text.append(
 | |
| 			"\n\n" + tr::lng_manage_linked_channel_private(tr::now));
 | |
| 	}
 | |
| 	if (!chat->isPublic()) {
 | |
| 		text.append(
 | |
| 			"\n\n" + tr::lng_manage_discussion_group_private(tr::now));
 | |
| 		if (chat->hiddenPreHistory()) {
 | |
| 			text.append("\n\n");
 | |
| 			text.append(tr::lng_manage_discussion_group_warning(
 | |
| 				tr::now,
 | |
| 				Ui::Text::RichLangValue));
 | |
| 		}
 | |
| 	}
 | |
| 	const auto sure = [=](Fn<void()> &&close) {
 | |
| 		close();
 | |
| 		const auto onstack = _callback;
 | |
| 		onstack(chat);
 | |
| 	};
 | |
| 	delegate()->peerListShowBox(
 | |
| 		Ui::MakeConfirmBox({
 | |
| 			.text = text,
 | |
| 			.confirmed = sure,
 | |
| 			.confirmText = tr::lng_manage_discussion_group_link(tr::now),
 | |
| 		}),
 | |
| 		Ui::LayerOption::KeepOther);
 | |
| }
 | |
| 
 | |
| void Controller::choose(not_null<ChatData*> chat) {
 | |
| 	auto text = tr::lng_manage_discussion_group_sure(
 | |
| 		tr::now,
 | |
| 		lt_group,
 | |
| 		Ui::Text::Bold(chat->name()),
 | |
| 		lt_channel,
 | |
| 		Ui::Text::Bold(_channel->name()),
 | |
| 		Ui::Text::WithEntities);
 | |
| 	if (!_channel->isPublic()) {
 | |
| 		text.append("\n\n" + tr::lng_manage_linked_channel_private(tr::now));
 | |
| 	}
 | |
| 	text.append("\n\n" + tr::lng_manage_discussion_group_private(tr::now));
 | |
| 	text.append("\n\n");
 | |
| 	text.append(tr::lng_manage_discussion_group_warning(
 | |
| 		tr::now,
 | |
| 		Ui::Text::RichLangValue));
 | |
| 	const auto sure = [=](Fn<void()> &&close) {
 | |
| 		close();
 | |
| 		const auto done = [=](not_null<ChannelData*> chat) {
 | |
| 			const auto onstack = _callback;
 | |
| 			onstack(chat);
 | |
| 		};
 | |
| 		chat->session().api().migrateChat(chat, crl::guard(this, done));
 | |
| 	};
 | |
| 	delegate()->peerListShowBox(
 | |
| 		Ui::MakeConfirmBox({
 | |
| 			.text = text,
 | |
| 			.confirmed = sure,
 | |
| 			.confirmText = tr::lng_manage_discussion_group_link(tr::now),
 | |
| 		}),
 | |
| 		Ui::LayerOption::KeepOther);
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::RpWidget> SetupAbout(
 | |
| 		not_null<QWidget*> parent,
 | |
| 		not_null<ChannelData*> channel,
 | |
| 		ChannelData *chat) {
 | |
| 	auto about = object_ptr<Ui::FlatLabel>(
 | |
| 		parent,
 | |
| 		QString(),
 | |
| 		st::linkedChatAbout);
 | |
| 	about->setMarkedText([&] {
 | |
| 		if (!channel->isBroadcast()) {
 | |
| 			return tr::lng_manage_linked_channel_about(
 | |
| 				tr::now,
 | |
| 				lt_channel,
 | |
| 				Ui::Text::Bold(chat->name()),
 | |
| 				Ui::Text::WithEntities);
 | |
| 		} else if (chat != nullptr) {
 | |
| 			return tr::lng_manage_discussion_group_about_chosen(
 | |
| 				tr::now,
 | |
| 				lt_group,
 | |
| 				Ui::Text::Bold(chat->name()),
 | |
| 				Ui::Text::WithEntities);
 | |
| 		}
 | |
| 		return tr::lng_manage_discussion_group_about(
 | |
| 			tr::now,
 | |
| 			Ui::Text::WithEntities);
 | |
| 	}());
 | |
| 	return about;
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::RpWidget> SetupFooter(
 | |
| 		not_null<QWidget*> parent,
 | |
| 		not_null<ChannelData*> channel) {
 | |
| 	return object_ptr<Ui::FlatLabel>(
 | |
| 		parent,
 | |
| 		(channel->isBroadcast()
 | |
| 			? tr::lng_manage_discussion_group_posted
 | |
| 			: tr::lng_manage_linked_channel_posted)(),
 | |
| 		st::linkedChatAbout);
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::RpWidget> SetupCreateGroup(
 | |
| 		not_null<QWidget*> parent,
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<ChannelData*> channel,
 | |
| 		Fn<void(ChannelData*)> callback) {
 | |
| 	Expects(channel->isBroadcast());
 | |
| 
 | |
| 	auto result = object_ptr<Ui::SettingsButton>(
 | |
| 		parent,
 | |
| 		tr::lng_manage_discussion_group_create(
 | |
| 		) | Ui::Text::ToUpper(),
 | |
| 		st::infoCreateLinkedChatButton);
 | |
| 	result->addClickHandler([=] {
 | |
| 		const auto guarded = crl::guard(parent, callback);
 | |
| 		Window::Show(navigation).showBox(
 | |
| 			Box<GroupInfoBox>(
 | |
| 				navigation,
 | |
| 				GroupInfoBox::Type::Megagroup,
 | |
| 				channel->name() + " Chat",
 | |
| 				guarded),
 | |
| 			Ui::LayerOption::KeepOther);
 | |
| 	});
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::RpWidget> SetupUnlink(
 | |
| 		not_null<QWidget*> parent,
 | |
| 		not_null<ChannelData*> channel,
 | |
| 		Fn<void(ChannelData*)> callback) {
 | |
| 	auto result = object_ptr<Ui::SettingsButton>(
 | |
| 		parent,
 | |
| 		(channel->isBroadcast()
 | |
| 			? tr::lng_manage_discussion_group_unlink
 | |
| 			: tr::lng_manage_linked_channel_unlink)() | Ui::Text::ToUpper(),
 | |
| 		st::infoUnlinkChatButton);
 | |
| 	result->addClickHandler([=] {
 | |
| 		callback(nullptr);
 | |
| 	});
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::BoxContent> EditLinkedChatBox(
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<ChannelData*> channel,
 | |
| 		ChannelData *chat,
 | |
| 		std::vector<not_null<PeerData*>> &&chats,
 | |
| 		bool canEdit,
 | |
| 		Fn<void(ChannelData*)> callback) {
 | |
| 	Expects((channel->isBroadcast() && canEdit) || (chat != nullptr));
 | |
| 
 | |
| 	const auto init = [=](not_null<PeerListBox*> box) {
 | |
| 		auto above = object_ptr<Ui::VerticalLayout>(box);
 | |
| 		above->add(
 | |
| 			SetupAbout(above, channel, chat),
 | |
| 			st::linkedChatAboutPadding);
 | |
| 		if (!chat) {
 | |
| 			above->add(SetupCreateGroup(
 | |
| 				above,
 | |
| 				navigation,
 | |
| 				channel,
 | |
| 				callback));
 | |
| 		}
 | |
| 		box->peerListSetAboveWidget(std::move(above));
 | |
| 
 | |
| 		auto below = object_ptr<Ui::VerticalLayout>(box);
 | |
| 		if (chat && canEdit) {
 | |
| 			below->add(SetupUnlink(below, channel, callback));
 | |
| 		}
 | |
| 		below->add(
 | |
| 			SetupFooter(below, channel),
 | |
| 			st::linkedChatAboutPadding);
 | |
| 		box->peerListSetBelowWidget(std::move(below));
 | |
| 
 | |
| 		box->setTitle(channel->isBroadcast()
 | |
| 			? tr::lng_manage_discussion_group()
 | |
| 			: tr::lng_manage_linked_channel());
 | |
| 		box->addButton(tr::lng_close(), [=] { box->closeBox(); });
 | |
| 	};
 | |
| 	auto showHistoryCallback = [=](not_null<PeerData*> peer) {
 | |
| 		navigation->showPeerHistory(
 | |
| 			peer,
 | |
| 			Window::SectionShow::Way::ClearStack,
 | |
| 			ShowAtUnreadMsgId);
 | |
| 	};
 | |
| 	auto controller = std::make_unique<Controller>(
 | |
| 		navigation,
 | |
| 		channel,
 | |
| 		chat,
 | |
| 		std::move(chats),
 | |
| 		std::move(callback),
 | |
| 		std::move(showHistoryCallback));
 | |
| 	return Box<PeerListBox>(std::move(controller), init);
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| object_ptr<Ui::BoxContent> EditLinkedChatBox(
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<ChannelData*> channel,
 | |
| 		std::vector<not_null<PeerData*>> &&chats,
 | |
| 		Fn<void(ChannelData*)> callback) {
 | |
| 	return EditLinkedChatBox(
 | |
| 		navigation,
 | |
| 		channel,
 | |
| 		nullptr,
 | |
| 		std::move(chats),
 | |
| 		true,
 | |
| 		callback);
 | |
| }
 | |
| 
 | |
| object_ptr<Ui::BoxContent> EditLinkedChatBox(
 | |
| 		not_null<Window::SessionNavigation*> navigation,
 | |
| 		not_null<ChannelData*> channel,
 | |
| 		not_null<ChannelData*> chat,
 | |
| 		bool canEdit,
 | |
| 		Fn<void(ChannelData*)> callback) {
 | |
| 	return EditLinkedChatBox(
 | |
| 		navigation,
 | |
| 		channel,
 | |
| 		chat,
 | |
| 		{},
 | |
| 		canEdit,
 | |
| 		callback);
 | |
| }
 | |
| 
 | |
| void ShowForumForDiscussionError(
 | |
| 		not_null<Window::SessionNavigation*> navigation) {
 | |
| 	Ui::ShowMultilineToast({
 | |
| 		.parentOverride = Window::Show(navigation).toastParent(),
 | |
| 		.text = tr::lng_forum_topics_no_discussion(
 | |
| 			tr::now,
 | |
| 			Ui::Text::RichLangValue),
 | |
| 	});
 | |
| }
 | 
