diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b624419143..0b9ea0883b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1922,6 +1922,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_box_delete" = "Delete"; "lng_box_leave" = "Leave"; +"lng_upload_sure_stop" = "Are you sure you want to stop uploading your files?\n\nIf you do, you'll need to start over."; +"lng_upload_show_file" = "Show file"; + "lng_about_version" = "version {version}"; "lng_about_text1" = "Official free messaging app based on {api_link}\nfor speed and security."; "lng_about_text1_api" = "Telegram API"; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 299c02cf4f..e5bf010584 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -82,7 +82,7 @@ namespace App { if (quitting()) { return; } else if (Core::IsAppLaunched() - && Core::App().exportPreventsQuit()) { + && Core::App().preventsQuit()) { return; } setLaunchState(QuitRequested); diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 725a6c5150..848753e6ae 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -639,6 +639,24 @@ void Application::logout(Main::Account *account) { } } +void Application::logoutWithChecks(Main::Account *account) { + const auto weak = base::make_weak(account); + const auto retry = [=] { + if (const auto account = weak.get()) { + logoutWithChecks(account); + } + }; + if (!account || !account->sessionExists()) { + logout(account); + } else if (_exportManager->inProgress(&account->session())) { + _exportManager->stopWithConfirmation(retry); + } else if (account->session().uploadsInProgress()) { + account->session().uploadsStopWithConfirmation(retry); + } else { + logout(account); + } +} + void Application::forceLogOut( not_null account, const TextWithEntities &explanation) { @@ -749,6 +767,33 @@ bool Application::exportPreventsQuit() { return false; } +bool Application::uploadPreventsQuit() { + if (!_domain->started()) { + return false; + } + for (const auto &[index, account] : _domain->accounts()) { + if (!account->sessionExists()) { + continue; + } + if (account->session().uploadsInProgress()) { + account->session().uploadsStopWithConfirmation([=] { + for (const auto &[index, account] : _domain->accounts()) { + if (account->sessionExists()) { + account->session().uploadsStop(); + } + } + App::quit(); + }); + return true; + } + } + return false; +} + +bool Application::preventsQuit() { + return exportPreventsQuit() || uploadPreventsQuit(); +} + int Application::unreadBadge() const { return _domain->unreadBadge(); } diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index abffd5e5ee..bdca073b44 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -239,9 +239,11 @@ public: } void logout(Main::Account *account = nullptr); + void logoutWithChecks(Main::Account *account); void forceLogOut( not_null account, const TextWithEntities &explanation); + [[nodiscard]] bool uploadPreventsQuit(); void checkLocalTime(); void lockByPasscode(); void unlockPasscode(); @@ -253,6 +255,8 @@ public: void checkAutoLockIn(crl::time time); void localPasscodeChanged(); + [[nodiscard]] bool preventsQuit(); + [[nodiscard]] crl::time lastNonIdleTime() const; void updateNonIdle(); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index dbba2f1356..a6dc65bf0e 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -70,16 +70,11 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900; const ClickHandlerContext &context, not_null session) { if (const auto controller = context.sessionWindow.get()) { - return controller; - } - const auto &windows = session->windows(); - if (windows.empty()) { - session->domain().activate(&session->account()); - if (windows.empty()) { - return nullptr; + if (&controller->session() == session) { + return controller; } } - return windows.front(); + return session->tryResolveWindow(); } } // namespace diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 9df9724ff4..10401054bb 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -29,11 +29,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/stickers/data_stickers.h" #include "window/window_session_controller.h" +#include "window/window_controller.h" #include "window/window_lock_widgets.h" #include "base/unixtime.h" #include "calls/calls_instance.h" #include "support/support_helper.h" +#include "lang/lang_keys.h" +#include "core/application.h" #include "ui/text/text_utilities.h" +#include "ui/layers/generic_box.h" +#include "styles/style_layers.h" #ifndef TDESKTOP_DISABLE_SPELLCHECK #include "chat_helpers/spellchecker_common.h" @@ -347,9 +352,66 @@ void Session::addWindow(not_null controller) { }) | rpl::distinct_until_changed()); } +bool Session::uploadsInProgress() const { + return !!_uploader->currentUploadId(); +} + +void Session::uploadsStopWithConfirmation(Fn done) { + const auto window = Core::App().primaryWindow(); + if (!window) { + return; + } + const auto id = _uploader->currentUploadId(); + const auto exists = !!data().message(id); + auto box = Box([=](not_null box) { + const auto label = box->addRow( + object_ptr( + box.get(), + tr::lng_upload_sure_stop(), + st::boxLabel), + st::boxPadding + QMargins(0, 0, 0, st::boxPadding.bottom())); + box->setStyle(st::defaultBox); + box->addButton(tr::lng_selected_upload_stop(), [=] { + box->closeBox(); + + uploadsStop(); + if (done) { + done(); + } + }, st::attentionBoxButton); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + if (exists) { + box->addLeftButton(tr::lng_upload_show_file(), [=] { + box->closeBox(); + + if (const auto item = data().message(id)) { + if (const auto window = tryResolveWindow()) { + window->showPeerHistoryAtItem(item); + } + } + }); + } + }); + window->show(std::move(box)); +} + +void Session::uploadsStop() { + _uploader->cancelAll(); +} + auto Session::windows() const -> const base::flat_set> & { return _windows; } +Window::SessionController *Session::tryResolveWindow() const { + if (_windows.empty()) { + domain().activate(_account); + if (_windows.empty()) { + return nullptr; + } + } + return _windows.front(); +} + } // namespace Main diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 1443637713..eab34d4122 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -125,6 +125,7 @@ public: void addWindow(not_null controller); [[nodiscard]] auto windows() const -> const base::flat_set> &; + [[nodiscard]] Window::SessionController *tryResolveWindow() const; // Shortcuts. void notifyDownloaderTaskFinished(); @@ -157,6 +158,11 @@ public: // Can be called only right before ~Session. void finishLogout(); + // Uploads cancel with confirmation. + [[nodiscard]] bool uploadsInProgress() const; + void uploadsStopWithConfirmation(Fn done); + void uploadsStop(); + [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index 664c8e0cb3..e5c709290c 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -358,26 +358,12 @@ void Uploader::upload( void Uploader::currentFailed() { auto j = queue.find(uploadingId); if (j != queue.end()) { - if (j->second.type() == SendMediaType::Photo) { - _photoFailed.fire_copy(j->first); - } else if (j->second.type() == SendMediaType::File - || j->second.type() == SendMediaType::ThemeFile - || j->second.type() == SendMediaType::Audio) { - const auto document = session().data().document(j->second.id()); - if (document->uploading()) { - document->status = FileUploadFailed; - } - _documentFailed.fire_copy(j->first); - } else if (j->second.type() == SendMediaType::Secure) { - _secureFailed.fire_copy(j->first); - } else { - Unexpected("Type in Uploader::currentFailed."); - } + const auto [msgId, file] = std::move(*j); queue.erase(j); + notifyFailed(msgId, file); } - requestsSent.clear(); - docRequestsSent.clear(); + cancelRequests(); dcMap.clear(); uploadingId = FullMsgId(); sentSize = 0; @@ -388,6 +374,25 @@ void Uploader::currentFailed() { sendNext(); } +void Uploader::notifyFailed(FullMsgId id, const File &file) { + const auto type = file.type(); + if (type == SendMediaType::Photo) { + _photoFailed.fire_copy(id); + } else if (type == SendMediaType::File + || type == SendMediaType::ThemeFile + || type == SendMediaType::Audio) { + const auto document = session().data().document(file.id()); + if (document->uploading()) { + document->status = FileUploadFailed; + } + _documentFailed.fire_copy(id); + } else if (type == SendMediaType::Secure) { + _secureFailed.fire_copy(id); + } else { + Unexpected("Type in Uploader::currentFailed."); + } +} + void Uploader::stopSessions() { for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { _api->instance().stopSession(MTP::uploadDcId(i)); @@ -617,7 +622,6 @@ void Uploader::sendNext() { } void Uploader::cancel(const FullMsgId &msgId) { - uploaded.erase(msgId); if (uploadingId == msgId) { currentFailed(); } else { @@ -625,6 +629,24 @@ void Uploader::cancel(const FullMsgId &msgId) { } } +void Uploader::cancelAll() { + const auto single = queue.empty() ? uploadingId : queue.begin()->first; + if (!single) { + return; + } + _pausedId = single; + if (uploadingId) { + currentFailed(); + } + while (!queue.empty()) { + const auto [msgId, file] = std::move(*queue.begin()); + queue.erase(queue.begin()); + notifyFailed(msgId, file); + } + clear(); + unpause(); +} + void Uploader::pause(const FullMsgId &msgId) { _pausedId = msgId; } @@ -637,9 +659,7 @@ void Uploader::unpause() { void Uploader::confirm(const FullMsgId &msgId) { } -void Uploader::clear() { - uploaded.clear(); - queue.clear(); +void Uploader::cancelRequests() { for (const auto &requestData : requestsSent) { _api->request(requestData.first).cancel(); } @@ -648,6 +668,11 @@ void Uploader::clear() { _api->request(requestData.first).cancel(); } docRequestsSent.clear(); +} + +void Uploader::clear() { + queue.clear(); + cancelRequests(); dcMap.clear(); sentSize = 0; for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 5bb893f92f..5f80fa1d65 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -54,6 +54,10 @@ public: [[nodiscard]] Main::Session &session() const; + [[nodiscard]] FullMsgId currentUploadId() const { + return uploadingId; + } + void uploadMedia(const FullMsgId &msgId, const SendMediaReady &image); void upload( const FullMsgId &msgId, @@ -63,6 +67,7 @@ public: void pause(const FullMsgId &msgId); void confirm(const FullMsgId &msgId); + void cancelAll(); void clear(); rpl::producer photoReady() const { @@ -108,7 +113,9 @@ private: void processDocumentProgress(const FullMsgId &msgId); void processDocumentFailed(const FullMsgId &msgId); + void notifyFailed(FullMsgId id, const File &file); void currentFailed(); + void cancelRequests(); void sendProgressUpdate( not_null item, @@ -125,7 +132,6 @@ private: FullMsgId uploadingId; FullMsgId _pausedId; std::map queue; - std::map uploaded; base::Timer _nextTimer, _stopSessionsTimer; rpl::event_stream _photoReady; diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index fb4b1cd946..591390bfb8 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -395,19 +395,12 @@ void Controller::showLogoutConfirmation() { ? &sessionController()->session().account() : nullptr; const auto weak = base::make_weak(account); - const auto callback = [=] { - if (account && !weak) { - return; + const auto callback = [=](Fn close) { + if (!account || weak) { + Core::App().logoutWithChecks(account); } - if (account - && account->sessionExists() - && Core::App().exportManager().inProgress(&account->session())) { - Ui::hideLayer(); - Core::App().exportManager().stopWithConfirmation([=] { - Core::App().logout(account); - }); - } else { - Core::App().logout(account); + if (close) { + close(); } }; show(Box( diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index 52231e6756..2cd80c8ad7 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -116,6 +116,7 @@ private: MsgId singlePeerShowAtMsgId); void setupSideBar(); void sideBarChanged(); + void logoutWithChecks(Main::Account *account); void showBox( object_ptr content, diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 99a26ba0d7..7e306f06c2 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -350,7 +350,7 @@ void MainMenu::AccountButton::contextMenuEvent(QContextMenuEvent *e) { const auto session = _session; const auto callback = [=](Fn &&close) { close(); - Core::App().logout(&session->account()); + Core::App().logoutWithChecks(&session->account()); }; Ui::show(Box( tr::lng_sure_logout(tr::now), diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index fcde0abd3e..fff1fc4f09 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_replies_section.h" #include "history/view/history_view_react_button.h" #include "history/view/history_view_reactions.h" +#include "history/view/history_view_scheduled_section.h" #include "media/player/media_player_instance.h" #include "media/view/media_view_open_common.h" #include "data/data_document_resolver.h" @@ -1320,10 +1321,16 @@ void SessionController::showPeerHistoryAtItem( _window->invokeForSessionController( &item->history()->peer->session().account(), [=](not_null controller) { - controller->showPeerHistory( - item->history()->peer, - SectionShow::Way::ClearStack, - item->id); + if (item->isScheduled()) { + controller->showSection( + std::make_shared( + item->history())); + } else { + controller->showPeerHistory( + item->history()->peer, + SectionShow::Way::ClearStack, + item->id); + } }); }