1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "boxes/peers/edit_linked_chat_box.h"
9 
10 #include "lang/lang_keys.h"
11 #include "data/data_channel.h"
12 #include "data/data_chat.h"
13 #include "data/data_changes.h"
14 #include "ui/widgets/labels.h"
15 #include "ui/widgets/buttons.h"
16 #include "ui/wrap/vertical_layout.h"
17 #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
18 #include "boxes/peer_list_box.h"
19 #include "ui/boxes/confirm_box.h"
20 #include "boxes/add_contact_box.h"
21 #include "apiwrap.h"
22 #include "facades.h"
23 #include "main/main_session.h"
24 #include "styles/style_layers.h"
25 #include "styles/style_boxes.h"
26 #include "styles/style_info.h"
27 
28 namespace {
29 
30 constexpr auto kEnableSearchRowsCount = 10;
31 
32 class Controller : public PeerListController, public base::has_weak_ptr {
33 public:
34 	Controller(
35 		not_null<ChannelData*> channel,
36 		ChannelData *chat,
37 		const std::vector<not_null<PeerData*>> &chats,
38 		Fn<void(ChannelData*)> callback);
39 
40 	Main::Session &session() const override;
41 	void prepare() override;
42 	void rowClicked(not_null<PeerListRow*> row) override;
43 	int contentWidth() const override;
44 
45 private:
46 	void choose(not_null<ChannelData*> chat);
47 	void choose(not_null<ChatData*> chat);
48 
49 	not_null<ChannelData*> _channel;
50 	ChannelData *_chat = nullptr;
51 	std::vector<not_null<PeerData*>> _chats;
52 	Fn<void(ChannelData*)> _callback;
53 
54 	ChannelData *_waitForFull = nullptr;
55 
56 };
57 
Controller(not_null<ChannelData * > channel,ChannelData * chat,const std::vector<not_null<PeerData * >> & chats,Fn<void (ChannelData *)> callback)58 Controller::Controller(
59 	not_null<ChannelData*> channel,
60 	ChannelData *chat,
61 	const std::vector<not_null<PeerData*>> &chats,
62 	Fn<void(ChannelData*)> callback)
63 : _channel(channel)
64 , _chat(chat)
65 , _chats(std::move(chats))
66 , _callback(std::move(callback)) {
67 	channel->session().changes().peerUpdates(
68 		Data::PeerUpdate::Flag::FullInfo
69 	) | rpl::filter([=](const Data::PeerUpdate &update) {
70 		return (update.peer == _waitForFull);
71 	}) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
72 		choose(std::exchange(_waitForFull, nullptr));
73 	}, lifetime());
74 }
75 
session() const76 Main::Session &Controller::session() const {
77 	return _channel->session();
78 }
79 
contentWidth() const80 int Controller::contentWidth() const {
81 	return st::boxWidth;
82 }
83 
prepare()84 void Controller::prepare() {
85 	const auto appendRow = [&](not_null<PeerData*> chat) {
86 		if (delegate()->peerListFindRow(chat->id.value)) {
87 			return;
88 		}
89 		auto row = std::make_unique<PeerListRow>(chat);
90 		const auto username = chat->userName();
91 		row->setCustomStatus(!username.isEmpty()
92 			? ('@' + username)
93 			: (chat->isChannel() && !chat->isMegagroup())
94 			? tr::lng_manage_linked_channel_private_status(tr::now)
95 			: tr::lng_manage_discussion_group_private_status(tr::now));
96 		delegate()->peerListAppendRow(std::move(row));
97 	};
98 	if (_chat) {
99 		appendRow(_chat);
100 	} else {
101 		for (const auto chat : _chats) {
102 			appendRow(chat);
103 		}
104 		if (_chats.size() >= kEnableSearchRowsCount) {
105 			delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
106 		}
107 	}
108 }
109 
rowClicked(not_null<PeerListRow * > row)110 void Controller::rowClicked(not_null<PeerListRow*> row) {
111 	if (_chat != nullptr) {
112 		Ui::showPeerHistory(_chat, ShowAtUnreadMsgId);
113 		return;
114 	}
115 	const auto peer = row->peer();
116 	if (const auto channel = peer->asChannel()) {
117 		if (channel->wasFullUpdated()) {
118 			choose(channel);
119 			return;
120 		}
121 		_waitForFull = channel;
122 		channel->updateFull();
123 	} else if (const auto chat = peer->asChat()) {
124 		choose(chat);
125 	}
126 }
127 
choose(not_null<ChannelData * > chat)128 void Controller::choose(not_null<ChannelData*> chat) {
129 	auto text = tr::lng_manage_discussion_group_sure(
130 		tr::now,
131 		lt_group,
132 		Ui::Text::Bold(chat->name),
133 		lt_channel,
134 		Ui::Text::Bold(_channel->name),
135 		Ui::Text::WithEntities);
136 	if (!_channel->isPublic()) {
137 		text.append(
138 			"\n\n" + tr::lng_manage_linked_channel_private(tr::now));
139 	}
140 	if (!chat->isPublic()) {
141 		text.append(
142 			"\n\n" + tr::lng_manage_discussion_group_private(tr::now));
143 		if (chat->hiddenPreHistory()) {
144 			text.append("\n\n");
145 			text.append(tr::lng_manage_discussion_group_warning(
146 				tr::now,
147 				Ui::Text::RichLangValue));
148 		}
149 	}
150 	const auto sure = [=](Fn<void()> &&close) {
151 		close();
152 		const auto onstack = _callback;
153 		onstack(chat);
154 	};
155 	Ui::show(
156 		Box<Ui::ConfirmBox>(
157 			text,
158 			tr::lng_manage_discussion_group_link(tr::now),
159 			sure),
160 		Ui::LayerOption::KeepOther);
161 }
162 
choose(not_null<ChatData * > chat)163 void Controller::choose(not_null<ChatData*> chat) {
164 	auto text = tr::lng_manage_discussion_group_sure(
165 		tr::now,
166 		lt_group,
167 		Ui::Text::Bold(chat->name),
168 		lt_channel,
169 		Ui::Text::Bold(_channel->name),
170 		Ui::Text::WithEntities);
171 	if (!_channel->isPublic()) {
172 		text.append("\n\n" + tr::lng_manage_linked_channel_private(tr::now));
173 	}
174 	text.append("\n\n" + tr::lng_manage_discussion_group_private(tr::now));
175 	text.append("\n\n");
176 	text.append(tr::lng_manage_discussion_group_warning(
177 		tr::now,
178 		Ui::Text::RichLangValue));
179 	const auto sure = [=](Fn<void()> &&close) {
180 		close();
181 		const auto done = [=](not_null<ChannelData*> chat) {
182 			const auto onstack = _callback;
183 			onstack(chat);
184 		};
185 		chat->session().api().migrateChat(chat, crl::guard(this, done));
186 	};
187 	Ui::show(
188 		Box<Ui::ConfirmBox>(
189 			text,
190 			tr::lng_manage_discussion_group_link(tr::now),
191 			sure),
192 		Ui::LayerOption::KeepOther);
193 }
194 
SetupAbout(not_null<QWidget * > parent,not_null<ChannelData * > channel,ChannelData * chat)195 object_ptr<Ui::RpWidget> SetupAbout(
196 		not_null<QWidget*> parent,
197 		not_null<ChannelData*> channel,
198 		ChannelData *chat) {
199 	auto about = object_ptr<Ui::FlatLabel>(
200 		parent,
201 		QString(),
202 		st::linkedChatAbout);
203 	about->setMarkedText([&] {
204 		if (!channel->isBroadcast()) {
205 			return tr::lng_manage_linked_channel_about(
206 				tr::now,
207 				lt_channel,
208 				Ui::Text::Bold(chat->name),
209 				Ui::Text::WithEntities);
210 		} else if (chat != nullptr) {
211 			return tr::lng_manage_discussion_group_about_chosen(
212 				tr::now,
213 				lt_group,
214 				Ui::Text::Bold(chat->name),
215 				Ui::Text::WithEntities);
216 		}
217 		return tr::lng_manage_discussion_group_about(
218 			tr::now,
219 			Ui::Text::WithEntities);
220 	}());
221 	return about;
222 }
223 
SetupFooter(not_null<QWidget * > parent,not_null<ChannelData * > channel)224 object_ptr<Ui::RpWidget> SetupFooter(
225 		not_null<QWidget*> parent,
226 		not_null<ChannelData*> channel) {
227 	return object_ptr<Ui::FlatLabel>(
228 		parent,
229 		(channel->isBroadcast()
230 			? tr::lng_manage_discussion_group_posted
231 			: tr::lng_manage_linked_channel_posted)(),
232 		st::linkedChatAbout);
233 }
234 
SetupCreateGroup(not_null<QWidget * > parent,not_null<Window::SessionNavigation * > navigation,not_null<ChannelData * > channel,Fn<void (ChannelData *)> callback)235 object_ptr<Ui::RpWidget> SetupCreateGroup(
236 		not_null<QWidget*> parent,
237 		not_null<Window::SessionNavigation*> navigation,
238 		not_null<ChannelData*> channel,
239 		Fn<void(ChannelData*)> callback) {
240 	Expects(channel->isBroadcast());
241 
242 	auto result = object_ptr<Ui::SettingsButton>(
243 		parent,
244 		tr::lng_manage_discussion_group_create(
245 		) | Ui::Text::ToUpper(),
246 		st::infoCreateLinkedChatButton);
247 	result->addClickHandler([=] {
248 		const auto guarded = crl::guard(parent, callback);
249 		Ui::show(
250 			Box<GroupInfoBox>(
251 				navigation,
252 				GroupInfoBox::Type::Megagroup,
253 				channel->name + " Chat",
254 				guarded),
255 			Ui::LayerOption::KeepOther);
256 	});
257 	return result;
258 }
259 
SetupUnlink(not_null<QWidget * > parent,not_null<ChannelData * > channel,Fn<void (ChannelData *)> callback)260 object_ptr<Ui::RpWidget> SetupUnlink(
261 		not_null<QWidget*> parent,
262 		not_null<ChannelData*> channel,
263 		Fn<void(ChannelData*)> callback) {
264 	auto result = object_ptr<Ui::SettingsButton>(
265 		parent,
266 		(channel->isBroadcast()
267 			? tr::lng_manage_discussion_group_unlink
268 			: tr::lng_manage_linked_channel_unlink)() | Ui::Text::ToUpper(),
269 		st::infoUnlinkChatButton);
270 	result->addClickHandler([=] {
271 		callback(nullptr);
272 	});
273 	return result;
274 }
275 
EditLinkedChatBox(not_null<Window::SessionNavigation * > navigation,not_null<ChannelData * > channel,ChannelData * chat,std::vector<not_null<PeerData * >> && chats,bool canEdit,Fn<void (ChannelData *)> callback)276 object_ptr<Ui::BoxContent> EditLinkedChatBox(
277 		not_null<Window::SessionNavigation*> navigation,
278 		not_null<ChannelData*> channel,
279 		ChannelData *chat,
280 		std::vector<not_null<PeerData*>> &&chats,
281 		bool canEdit,
282 		Fn<void(ChannelData*)> callback) {
283 	Expects((channel->isBroadcast() && canEdit) || (chat != nullptr));
284 
285 	const auto init = [=](not_null<PeerListBox*> box) {
286 		auto above = object_ptr<Ui::VerticalLayout>(box);
287 		above->add(
288 			SetupAbout(above, channel, chat),
289 			st::linkedChatAboutPadding);
290 		if (!chat) {
291 			above->add(SetupCreateGroup(
292 				above,
293 				navigation,
294 				channel,
295 				callback));
296 		}
297 		box->peerListSetAboveWidget(std::move(above));
298 
299 		auto below = object_ptr<Ui::VerticalLayout>(box);
300 		if (chat && canEdit) {
301 			below->add(SetupUnlink(below, channel, callback));
302 		}
303 		below->add(
304 			SetupFooter(below, channel),
305 			st::linkedChatAboutPadding);
306 		box->peerListSetBelowWidget(std::move(below));
307 
308 		box->setTitle(channel->isBroadcast()
309 			? tr::lng_manage_discussion_group()
310 			: tr::lng_manage_linked_channel());
311 		box->addButton(tr::lng_close(), [=] { box->closeBox(); });
312 	};
313 	auto controller = std::make_unique<Controller>(
314 		channel,
315 		chat,
316 		std::move(chats),
317 		std::move(callback));
318 	return Box<PeerListBox>(std::move(controller), init);
319 }
320 
321 } // namespace
322 
EditLinkedChatBox(not_null<Window::SessionNavigation * > navigation,not_null<ChannelData * > channel,std::vector<not_null<PeerData * >> && chats,Fn<void (ChannelData *)> callback)323 object_ptr<Ui::BoxContent> EditLinkedChatBox(
324 		not_null<Window::SessionNavigation*> navigation,
325 		not_null<ChannelData*> channel,
326 		std::vector<not_null<PeerData*>> &&chats,
327 		Fn<void(ChannelData*)> callback) {
328 	return EditLinkedChatBox(
329 		navigation,
330 		channel,
331 		nullptr,
332 		std::move(chats),
333 		true,
334 		callback);
335 }
336 
EditLinkedChatBox(not_null<Window::SessionNavigation * > navigation,not_null<ChannelData * > channel,not_null<ChannelData * > chat,bool canEdit,Fn<void (ChannelData *)> callback)337 object_ptr<Ui::BoxContent> EditLinkedChatBox(
338 		not_null<Window::SessionNavigation*> navigation,
339 		not_null<ChannelData*> channel,
340 		not_null<ChannelData*> chat,
341 		bool canEdit,
342 		Fn<void(ChannelData*)> callback) {
343 	return EditLinkedChatBox(
344 		navigation,
345 		channel,
346 		chat,
347 		{},
348 		canEdit,
349 		callback);
350 }
351