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_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";

View File

@ -82,7 +82,7 @@ namespace App {
if (quitting()) {
return;
} else if (Core::IsAppLaunched()
&& Core::App().exportPreventsQuit()) {
&& Core::App().preventsQuit()) {
return;
}
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(
not_null<Main::Account*> 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();
}

View File

@ -239,9 +239,11 @@ public:
}
void logout(Main::Account *account = nullptr);
void logoutWithChecks(Main::Account *account);
void forceLogOut(
not_null<Main::Account*> 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();

View File

@ -70,16 +70,11 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900;
const ClickHandlerContext &context,
not_null<Main::Session*> 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

View File

@ -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<Window::SessionController*> controller) {
}) | 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
-> const base::flat_set<not_null<Window::SessionController*>> & {
return _windows;
}
Window::SessionController *Session::tryResolveWindow() const {
if (_windows.empty()) {
domain().activate(_account);
if (_windows.empty()) {
return nullptr;
}
}
return _windows.front();
}
} // namespace Main

View File

@ -125,6 +125,7 @@ public:
void addWindow(not_null<Window::SessionController*> controller);
[[nodiscard]] auto windows() const
-> const base::flat_set<not_null<Window::SessionController*>> &;
[[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<void()> done);
void uploadsStop();
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}

View File

@ -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) {

View File

@ -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<UploadedMedia> 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<HistoryItem*> item,
@ -125,7 +132,6 @@ private:
FullMsgId uploadingId;
FullMsgId _pausedId;
std::map<FullMsgId, File> queue;
std::map<FullMsgId, File> uploaded;
base::Timer _nextTimer, _stopSessionsTimer;
rpl::event_stream<UploadedMedia> _photoReady;

View File

@ -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<void()> 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<Ui::ConfirmBox>(

View File

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

View File

@ -350,7 +350,7 @@ void MainMenu::AccountButton::contextMenuEvent(QContextMenuEvent *e) {
const auto session = _session;
const auto callback = [=](Fn<void()> &&close) {
close();
Core::App().logout(&session->account());
Core::App().logoutWithChecks(&session->account());
};
Ui::show(Box<Ui::ConfirmBox>(
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_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<SessionController*> controller) {
controller->showPeerHistory(
item->history()->peer,
SectionShow::Way::ClearStack,
item->id);
if (item->isScheduled()) {
controller->showSection(
std::make_shared<HistoryView::ScheduledMemento>(
item->history()));
} else {
controller->showPeerHistory(
item->history()->peer,
SectionShow::Way::ClearStack,
item->id);
}
});
}