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