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 "window/window_controller.h"
9 
10 #include "api/api_updates.h"
11 #include "core/application.h"
12 #include "core/click_handler_types.h"
13 #include "export/export_manager.h"
14 #include "ui/platform/ui_platform_window.h"
15 #include "platform/platform_window_title.h"
16 #include "main/main_account.h"
17 #include "main/main_domain.h"
18 #include "main/main_session.h"
19 #include "main/main_session_settings.h"
20 #include "main/main_app_config.h"
21 #include "media/view/media_view_open_common.h"
22 #include "intro/intro_widget.h"
23 #include "mtproto/mtproto_config.h"
24 #include "ui/layers/box_content.h"
25 #include "ui/layers/layer_widget.h"
26 #include "ui/toast/toast.h"
27 #include "ui/emoji_config.h"
28 #include "chat_helpers/emoji_sets_manager.h"
29 #include "window/window_session_controller.h"
30 #include "window/themes/window_theme.h"
31 #include "window/themes/window_theme_editor.h"
32 #include "ui/boxes/confirm_box.h"
33 #include "mainwindow.h"
34 #include "apiwrap.h" // ApiWrap::acceptTerms.
35 #include "facades.h"
36 #include "styles/style_layers.h"
37 
38 #include <QtGui/QWindow>
39 #include <QtGui/QScreen>
40 
41 namespace Window {
42 
Controller()43 Controller::Controller()
44 : _widget(this)
45 , _adaptive(std::make_unique<Adaptive>())
46 , _isActiveTimer([=] { updateIsActive(); }) {
47 	_widget.init();
48 }
49 
~Controller()50 Controller::~Controller() {
51 	// We want to delete all widgets before the _sessionController.
52 	_widget.ui_hideSettingsAndLayer(anim::type::instant);
53 	_widget.clearWidgets();
54 }
55 
showAccount(not_null<Main::Account * > account)56 void Controller::showAccount(not_null<Main::Account*> account) {
57 	const auto prevSessionUniqueId = (_account && _account->sessionExists())
58 		? _account->session().uniqueId()
59 		: 0;
60 	_accountLifetime.destroy();
61 	_account = account;
62 
63 	const auto updateOnlineOfPrevSesssion = crl::guard(_account, [=] {
64 		if (!prevSessionUniqueId) {
65 			return;
66 		}
67 		for (auto &[index, account] : _account->domain().accounts()) {
68 			if (const auto anotherSession = account->maybeSession()) {
69 				if (anotherSession->uniqueId() == prevSessionUniqueId) {
70 					anotherSession->updates().updateOnline(crl::now());
71 					return;
72 				}
73 			}
74 		}
75 	});
76 
77 	_account->sessionValue(
78 	) | rpl::start_with_next([=](Main::Session *session) {
79 		const auto was = base::take(_sessionController);
80 		_sessionController = session
81 			? std::make_unique<SessionController>(session, this)
82 			: nullptr;
83 		if (_sessionController) {
84 			_sessionController->filtersMenuChanged(
85 			) | rpl::start_with_next([=] {
86 				sideBarChanged();
87 			}, _sessionController->lifetime());
88 		}
89 		if (session && session->settings().dialogsFiltersEnabled()) {
90 			_sessionController->toggleFiltersMenu(true);
91 		} else {
92 			sideBarChanged();
93 		}
94 		_widget.updateWindowIcon();
95 		if (session) {
96 			setupMain();
97 
98 			session->updates().isIdleValue(
99 			) | rpl::filter([=](bool idle) {
100 				return !idle;
101 			}) | rpl::start_with_next([=] {
102 				widget()->checkHistoryActivation();
103 			}, _sessionController->lifetime());
104 
105 			session->termsLockValue(
106 			) | rpl::start_with_next([=] {
107 				checkLockByTerms();
108 				_widget.updateGlobalMenu();
109 			}, _sessionController->lifetime());
110 		} else {
111 			setupIntro();
112 			_widget.updateGlobalMenu();
113 		}
114 
115 		crl::on_main(updateOnlineOfPrevSesssion);
116 	}, _accountLifetime);
117 }
118 
checkLockByTerms()119 void Controller::checkLockByTerms() {
120 	const auto data = account().sessionExists()
121 		? account().session().termsLocked()
122 		: std::nullopt;
123 	if (!data) {
124 		if (_termsBox) {
125 			_termsBox->closeBox();
126 		}
127 		return;
128 	}
129 	Ui::hideSettingsAndLayer(anim::type::instant);
130 	const auto box = show(Box<TermsBox>(
131 		*data,
132 		tr::lng_terms_agree(),
133 		tr::lng_terms_decline()));
134 
135 	box->setCloseByEscape(false);
136 	box->setCloseByOutsideClick(false);
137 
138 	const auto id = data->id;
139 	box->agreeClicks(
140 	) | rpl::start_with_next([=] {
141 		const auto mention = box ? box->lastClickedMention() : QString();
142 		box->closeBox();
143 		if (const auto session = account().maybeSession()) {
144 			session->api().acceptTerms(id);
145 			session->unlockTerms();
146 			if (!mention.isEmpty()) {
147 				MentionClickHandler(mention).onClick({});
148 			}
149 		}
150 	}, box->lifetime());
151 
152 	box->cancelClicks(
153 	) | rpl::start_with_next([=] {
154 		showTermsDecline();
155 	}, box->lifetime());
156 
157 	QObject::connect(box, &QObject::destroyed, [=] {
158 		crl::on_main(widget(), [=] { checkLockByTerms(); });
159 	});
160 
161 	_termsBox = box;
162 }
163 
showTermsDecline()164 void Controller::showTermsDecline() {
165 	const auto box = show(
166 		Box<Window::TermsBox>(
167 			TextWithEntities{ tr::lng_terms_update_sorry(tr::now) },
168 			tr::lng_terms_decline_and_delete(),
169 			tr::lng_terms_back(),
170 			true),
171 		Ui::LayerOption::KeepOther);
172 
173 	box->agreeClicks(
174 	) | rpl::start_with_next([=] {
175 		if (box) {
176 			box->closeBox();
177 		}
178 		showTermsDelete();
179 	}, box->lifetime());
180 
181 	box->cancelClicks(
182 	) | rpl::start_with_next([=] {
183 		if (box) {
184 			box->closeBox();
185 		}
186 	}, box->lifetime());
187 }
188 
showTermsDelete()189 void Controller::showTermsDelete() {
190 	const auto deleteByTerms = [=] {
191 		if (const auto session = account().maybeSession()) {
192 			session->termsDeleteNow();
193 		} else {
194 			Ui::hideLayer();
195 		}
196 	};
197 	show(
198 		Box<Ui::ConfirmBox>(
199 			tr::lng_terms_delete_warning(tr::now),
200 			tr::lng_terms_delete_now(tr::now),
201 			st::attentionBoxButton,
202 			deleteByTerms),
203 		Ui::LayerOption::KeepOther);
204 }
205 
finishFirstShow()206 void Controller::finishFirstShow() {
207 	_widget.finishFirstShow();
208 	checkThemeEditor();
209 }
210 
locked() const211 bool Controller::locked() const {
212 	if (Core::App().passcodeLocked()) {
213 		return true;
214 	} else if (const auto controller = sessionController()) {
215 		return controller->session().termsLocked().has_value();
216 	}
217 	return false;
218 }
219 
checkThemeEditor()220 void Controller::checkThemeEditor() {
221 	using namespace Window::Theme;
222 
223 	if (const auto editing = Background()->editingTheme()) {
224 		showRightColumn(Box<Editor>(this, *editing));
225 	}
226 }
227 
setupPasscodeLock()228 void Controller::setupPasscodeLock() {
229 	_widget.setupPasscodeLock();
230 }
231 
clearPasscodeLock()232 void Controller::clearPasscodeLock() {
233 	if (!_account) {
234 		showAccount(&Core::App().activeAccount());
235 	} else {
236 		_widget.clearPasscodeLock();
237 	}
238 }
239 
setupIntro()240 void Controller::setupIntro() {
241 	_widget.setupIntro(Core::App().domain().maybeLastOrSomeAuthedAccount()
242 		? Intro::EnterPoint::Qr
243 		: Intro::EnterPoint::Start);
244 }
245 
setupMain()246 void Controller::setupMain() {
247 	Expects(_sessionController != nullptr);
248 
249 	_widget.setupMain();
250 
251 	if (const auto id = Ui::Emoji::NeedToSwitchBackToId()) {
252 		Ui::Emoji::LoadAndSwitchTo(&_sessionController->session(), id);
253 	}
254 }
255 
showSettings()256 void Controller::showSettings() {
257 	_widget.showSettings();
258 }
259 
verticalShadowTop() const260 int Controller::verticalShadowTop() const {
261 	return (Platform::NativeTitleRequiresShadow()
262 		&& Ui::Platform::NativeWindowFrameSupported()
263 		&& Core::App().settings().nativeWindowFrame())
264 		? st::lineWidth
265 		: 0;
266 }
267 
showToast(const QString & text)268 void Controller::showToast(const QString &text) {
269 	Ui::Toast::Show(_widget.bodyWidget(), text);
270 }
271 
showLayer(std::unique_ptr<Ui::LayerWidget> && layer,Ui::LayerOptions options,anim::type animated)272 void Controller::showLayer(
273 		std::unique_ptr<Ui::LayerWidget> &&layer,
274 		Ui::LayerOptions options,
275 		anim::type animated) {
276 	_widget.showLayer(std::move(layer), options, animated);
277 }
278 
showBox(object_ptr<Ui::BoxContent> content,Ui::LayerOptions options,anim::type animated)279 void Controller::showBox(
280 		object_ptr<Ui::BoxContent> content,
281 		Ui::LayerOptions options,
282 		anim::type animated) {
283 	_widget.ui_showBox(std::move(content), options, animated);
284 }
285 
showRightColumn(object_ptr<TWidget> widget)286 void Controller::showRightColumn(object_ptr<TWidget> widget) {
287 	_widget.showRightColumn(std::move(widget));
288 }
289 
sideBarChanged()290 void Controller::sideBarChanged() {
291 	_widget.recountGeometryConstraints();
292 }
293 
activate()294 void Controller::activate() {
295 	_widget.activate();
296 }
297 
reActivate()298 void Controller::reActivate() {
299 	_widget.reActivateWindow();
300 }
301 
updateIsActiveFocus()302 void Controller::updateIsActiveFocus() {
303 	_isActiveTimer.callOnce(sessionController()
304 		? sessionController()->session().serverConfig().onlineFocusTimeout
305 		: crl::time(1000));
306 }
307 
updateIsActiveBlur()308 void Controller::updateIsActiveBlur() {
309 	_isActiveTimer.callOnce(sessionController()
310 		? sessionController()->session().serverConfig().offlineBlurTimeout
311 		: crl::time(1000));
312 }
313 
updateIsActive()314 void Controller::updateIsActive() {
315 	_widget.updateIsActive();
316 }
317 
minimize()318 void Controller::minimize() {
319 	if (Core::App().settings().workMode()
320 			== Core::Settings::WorkMode::TrayOnly) {
321 		_widget.minimizeToTray();
322 	} else {
323 		_widget.setWindowState(_widget.windowState() | Qt::WindowMinimized);
324 	}
325 }
326 
close()327 void Controller::close() {
328 	_widget.close();
329 }
330 
preventOrInvoke(Fn<void ()> && callback)331 void Controller::preventOrInvoke(Fn<void()> &&callback) {
332 	_widget.preventOrInvoke(std::move(callback));
333 }
334 
invokeForSessionController(not_null<Main::Account * > account,Fn<void (not_null<SessionController * >)> && callback)335 void Controller::invokeForSessionController(
336 		not_null<Main::Account*> account,
337 		Fn<void(not_null<SessionController*>)> &&callback) {
338 	_account->domain().activate(std::move(account));
339 	if (_sessionController) {
340 		callback(_sessionController.get());
341 	}
342 }
343 
getPointForCallPanelCenter() const344 QPoint Controller::getPointForCallPanelCenter() const {
345 	Expects(_widget.windowHandle() != nullptr);
346 
347 	return _widget.isActive()
348 		? _widget.geometry().center()
349 		: _widget.windowHandle()->screen()->geometry().center();
350 }
351 
showLogoutConfirmation()352 void Controller::showLogoutConfirmation() {
353 	const auto account = Core::App().passcodeLocked()
354 		? nullptr
355 		: sessionController()
356 		? &sessionController()->session().account()
357 		: nullptr;
358 	const auto weak = base::make_weak(account);
359 	const auto callback = [=] {
360 		if (account && !weak) {
361 			return;
362 		}
363 		if (account
364 			&& account->sessionExists()
365 			&& Core::App().exportManager().inProgress(&account->session())) {
366 			Ui::hideLayer();
367 			Core::App().exportManager().stopWithConfirmation([=] {
368 				Core::App().logout(account);
369 			});
370 		} else {
371 			Core::App().logout(account);
372 		}
373 	};
374 	show(Box<Ui::ConfirmBox>(
375 		tr::lng_sure_logout(tr::now),
376 		tr::lng_settings_logout(tr::now),
377 		st::attentionBoxButton,
378 		callback));
379 }
380 
adaptive() const381 Window::Adaptive &Controller::adaptive() const {
382 	return *_adaptive;
383 }
384 
openInMediaView(Media::View::OpenRequest && request)385 void Controller::openInMediaView(Media::View::OpenRequest &&request) {
386 	_openInMediaViewRequests.fire(std::move(request));
387 }
388 
openInMediaViewRequests() const389 auto Controller::openInMediaViewRequests() const
390 -> rpl::producer<Media::View::OpenRequest> {
391 	return _openInMediaViewRequests.events();
392 }
393 
lifetime()394 rpl::lifetime &Controller::lifetime() {
395 	return _lifetime;
396 }
397 
398 } // namespace Window
399