Add upload cancel confirmation on Quit and Log Out.

This commit is contained in:
John Preston 2022-01-26 12:41:27 +03:00
parent 8c349c0515
commit 6a3ad52aef
13 changed files with 195 additions and 48 deletions

View file

@ -1922,6 +1922,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_box_delete" = "Delete"; "lng_box_delete" = "Delete";
"lng_box_leave" = "Leave"; "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_version" = "version {version}";
"lng_about_text1" = "Official free messaging app based on {api_link}\nfor speed and security."; "lng_about_text1" = "Official free messaging app based on {api_link}\nfor speed and security.";
"lng_about_text1_api" = "Telegram API"; "lng_about_text1_api" = "Telegram API";

View file

@ -82,7 +82,7 @@ namespace App {
if (quitting()) { if (quitting()) {
return; return;
} else if (Core::IsAppLaunched() } else if (Core::IsAppLaunched()
&& Core::App().exportPreventsQuit()) { && Core::App().preventsQuit()) {
return; return;
} }
setLaunchState(QuitRequested); setLaunchState(QuitRequested);

View file

@ -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( void Application::forceLogOut(
not_null<Main::Account*> account, not_null<Main::Account*> account,
const TextWithEntities &explanation) { const TextWithEntities &explanation) {
@ -749,6 +767,33 @@ bool Application::exportPreventsQuit() {
return false; 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 { int Application::unreadBadge() const {
return _domain->unreadBadge(); return _domain->unreadBadge();
} }

View file

@ -239,9 +239,11 @@ public:
} }
void logout(Main::Account *account = nullptr); void logout(Main::Account *account = nullptr);
void logoutWithChecks(Main::Account *account);
void forceLogOut( void forceLogOut(
not_null<Main::Account*> account, not_null<Main::Account*> account,
const TextWithEntities &explanation); const TextWithEntities &explanation);
[[nodiscard]] bool uploadPreventsQuit();
void checkLocalTime(); void checkLocalTime();
void lockByPasscode(); void lockByPasscode();
void unlockPasscode(); void unlockPasscode();
@ -253,6 +255,8 @@ public:
void checkAutoLockIn(crl::time time); void checkAutoLockIn(crl::time time);
void localPasscodeChanged(); void localPasscodeChanged();
[[nodiscard]] bool preventsQuit();
[[nodiscard]] crl::time lastNonIdleTime() const; [[nodiscard]] crl::time lastNonIdleTime() const;
void updateNonIdle(); void updateNonIdle();

View file

@ -70,16 +70,11 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900;
const ClickHandlerContext &context, const ClickHandlerContext &context,
not_null<Main::Session*> session) { not_null<Main::Session*> session) {
if (const auto controller = context.sessionWindow.get()) { if (const auto controller = context.sessionWindow.get()) {
return controller; if (&controller->session() == session) {
} return controller;
const auto &windows = session->windows();
if (windows.empty()) {
session->domain().activate(&session->account());
if (windows.empty()) {
return nullptr;
} }
} }
return windows.front(); return session->tryResolveWindow();
} }
} // namespace } // namespace

View file

@ -29,11 +29,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "window/window_lock_widgets.h" #include "window/window_lock_widgets.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "support/support_helper.h" #include "support/support_helper.h"
#include "lang/lang_keys.h"
#include "core/application.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/layers/generic_box.h"
#include "styles/style_layers.h"
#ifndef TDESKTOP_DISABLE_SPELLCHECK #ifndef TDESKTOP_DISABLE_SPELLCHECK
#include "chat_helpers/spellchecker_common.h" #include "chat_helpers/spellchecker_common.h"
@ -347,9 +352,66 @@ void Session::addWindow(not_null<Window::SessionController*> controller) {
}) | rpl::distinct_until_changed()); }) | rpl::distinct_until_changed());
} }
bool Session::uploadsInProgress() const {
return !!_uploader->currentUploadId();
}
void Session::uploadsStopWithConfirmation(Fn<void()> 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<Ui::GenericBox*> box) {
const auto label = box->addRow(
object_ptr<Ui::FlatLabel>(
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 auto Session::windows() const
-> const base::flat_set<not_null<Window::SessionController*>> & { -> const base::flat_set<not_null<Window::SessionController*>> & {
return _windows; return _windows;
} }
Window::SessionController *Session::tryResolveWindow() const {
if (_windows.empty()) {
domain().activate(_account);
if (_windows.empty()) {
return nullptr;
}
}
return _windows.front();
}
} // namespace Main } // namespace Main

View file

@ -125,6 +125,7 @@ public:
void addWindow(not_null<Window::SessionController*> controller); void addWindow(not_null<Window::SessionController*> controller);
[[nodiscard]] auto windows() const [[nodiscard]] auto windows() const
-> const base::flat_set<not_null<Window::SessionController*>> &; -> const base::flat_set<not_null<Window::SessionController*>> &;
[[nodiscard]] Window::SessionController *tryResolveWindow() const;
// Shortcuts. // Shortcuts.
void notifyDownloaderTaskFinished(); void notifyDownloaderTaskFinished();
@ -157,6 +158,11 @@ public:
// Can be called only right before ~Session. // Can be called only right before ~Session.
void finishLogout(); void finishLogout();
// Uploads cancel with confirmation.
[[nodiscard]] bool uploadsInProgress() const;
void uploadsStopWithConfirmation(Fn<void()> done);
void uploadsStop();
[[nodiscard]] rpl::lifetime &lifetime() { [[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime; return _lifetime;
} }

View file

@ -358,26 +358,12 @@ void Uploader::upload(
void Uploader::currentFailed() { void Uploader::currentFailed() {
auto j = queue.find(uploadingId); auto j = queue.find(uploadingId);
if (j != queue.end()) { if (j != queue.end()) {
if (j->second.type() == SendMediaType::Photo) { const auto [msgId, file] = std::move(*j);
_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.");
}
queue.erase(j); queue.erase(j);
notifyFailed(msgId, file);
} }
requestsSent.clear(); cancelRequests();
docRequestsSent.clear();
dcMap.clear(); dcMap.clear();
uploadingId = FullMsgId(); uploadingId = FullMsgId();
sentSize = 0; sentSize = 0;
@ -388,6 +374,25 @@ void Uploader::currentFailed() {
sendNext(); 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() { void Uploader::stopSessions() {
for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { for (int i = 0; i < MTP::kUploadSessionsCount; ++i) {
_api->instance().stopSession(MTP::uploadDcId(i)); _api->instance().stopSession(MTP::uploadDcId(i));
@ -617,7 +622,6 @@ void Uploader::sendNext() {
} }
void Uploader::cancel(const FullMsgId &msgId) { void Uploader::cancel(const FullMsgId &msgId) {
uploaded.erase(msgId);
if (uploadingId == msgId) { if (uploadingId == msgId) {
currentFailed(); currentFailed();
} else { } 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) { void Uploader::pause(const FullMsgId &msgId) {
_pausedId = msgId; _pausedId = msgId;
} }
@ -637,9 +659,7 @@ void Uploader::unpause() {
void Uploader::confirm(const FullMsgId &msgId) { void Uploader::confirm(const FullMsgId &msgId) {
} }
void Uploader::clear() { void Uploader::cancelRequests() {
uploaded.clear();
queue.clear();
for (const auto &requestData : requestsSent) { for (const auto &requestData : requestsSent) {
_api->request(requestData.first).cancel(); _api->request(requestData.first).cancel();
} }
@ -648,6 +668,11 @@ void Uploader::clear() {
_api->request(requestData.first).cancel(); _api->request(requestData.first).cancel();
} }
docRequestsSent.clear(); docRequestsSent.clear();
}
void Uploader::clear() {
queue.clear();
cancelRequests();
dcMap.clear(); dcMap.clear();
sentSize = 0; sentSize = 0;
for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { for (int i = 0; i < MTP::kUploadSessionsCount; ++i) {

View file

@ -54,6 +54,10 @@ public:
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
[[nodiscard]] FullMsgId currentUploadId() const {
return uploadingId;
}
void uploadMedia(const FullMsgId &msgId, const SendMediaReady &image); void uploadMedia(const FullMsgId &msgId, const SendMediaReady &image);
void upload( void upload(
const FullMsgId &msgId, const FullMsgId &msgId,
@ -63,6 +67,7 @@ public:
void pause(const FullMsgId &msgId); void pause(const FullMsgId &msgId);
void confirm(const FullMsgId &msgId); void confirm(const FullMsgId &msgId);
void cancelAll();
void clear(); void clear();
rpl::producer<UploadedMedia> photoReady() const { rpl::producer<UploadedMedia> photoReady() const {
@ -108,7 +113,9 @@ private:
void processDocumentProgress(const FullMsgId &msgId); void processDocumentProgress(const FullMsgId &msgId);
void processDocumentFailed(const FullMsgId &msgId); void processDocumentFailed(const FullMsgId &msgId);
void notifyFailed(FullMsgId id, const File &file);
void currentFailed(); void currentFailed();
void cancelRequests();
void sendProgressUpdate( void sendProgressUpdate(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
@ -125,7 +132,6 @@ private:
FullMsgId uploadingId; FullMsgId uploadingId;
FullMsgId _pausedId; FullMsgId _pausedId;
std::map<FullMsgId, File> queue; std::map<FullMsgId, File> queue;
std::map<FullMsgId, File> uploaded;
base::Timer _nextTimer, _stopSessionsTimer; base::Timer _nextTimer, _stopSessionsTimer;
rpl::event_stream<UploadedMedia> _photoReady; rpl::event_stream<UploadedMedia> _photoReady;

View file

@ -395,19 +395,12 @@ void Controller::showLogoutConfirmation() {
? &sessionController()->session().account() ? &sessionController()->session().account()
: nullptr; : nullptr;
const auto weak = base::make_weak(account); const auto weak = base::make_weak(account);
const auto callback = [=] { const auto callback = [=](Fn<void()> close) {
if (account && !weak) { if (!account || weak) {
return; Core::App().logoutWithChecks(account);
} }
if (account if (close) {
&& account->sessionExists() close();
&& Core::App().exportManager().inProgress(&account->session())) {
Ui::hideLayer();
Core::App().exportManager().stopWithConfirmation([=] {
Core::App().logout(account);
});
} else {
Core::App().logout(account);
} }
}; };
show(Box<Ui::ConfirmBox>( show(Box<Ui::ConfirmBox>(

View file

@ -116,6 +116,7 @@ private:
MsgId singlePeerShowAtMsgId); MsgId singlePeerShowAtMsgId);
void setupSideBar(); void setupSideBar();
void sideBarChanged(); void sideBarChanged();
void logoutWithChecks(Main::Account *account);
void showBox( void showBox(
object_ptr<Ui::BoxContent> content, object_ptr<Ui::BoxContent> content,

View file

@ -350,7 +350,7 @@ void MainMenu::AccountButton::contextMenuEvent(QContextMenuEvent *e) {
const auto session = _session; const auto session = _session;
const auto callback = [=](Fn<void()> &&close) { const auto callback = [=](Fn<void()> &&close) {
close(); close();
Core::App().logout(&session->account()); Core::App().logoutWithChecks(&session->account());
}; };
Ui::show(Box<Ui::ConfirmBox>( Ui::show(Box<Ui::ConfirmBox>(
tr::lng_sure_logout(tr::now), tr::lng_sure_logout(tr::now),

View file

@ -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_replies_section.h"
#include "history/view/history_view_react_button.h" #include "history/view/history_view_react_button.h"
#include "history/view/history_view_reactions.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/player/media_player_instance.h"
#include "media/view/media_view_open_common.h" #include "media/view/media_view_open_common.h"
#include "data/data_document_resolver.h" #include "data/data_document_resolver.h"
@ -1320,10 +1321,16 @@ void SessionController::showPeerHistoryAtItem(
_window->invokeForSessionController( _window->invokeForSessionController(
&item->history()->peer->session().account(), &item->history()->peer->session().account(),
[=](not_null<SessionController*> controller) { [=](not_null<SessionController*> controller) {
controller->showPeerHistory( if (item->isScheduled()) {
item->history()->peer, controller->showSection(
SectionShow::Way::ClearStack, std::make_shared<HistoryView::ScheduledMemento>(
item->id); item->history()));
} else {
controller->showPeerHistory(
item->history()->peer,
SectionShow::Way::ClearStack,
item->id);
}
}); });
} }