diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 55bbd266553980..5f4b5e15e112da 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1697,6 +1697,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_export_total_files" = "Total files: {count}."; "lng_export_total_size" = "Total size: {size}."; "lng_export_folder" = "Choose export folder"; +"lng_export_invalid" = "Sorry, you have started a new data export, so this data export is now cancelled."; +"lng_export_delay" = "Sorry, for security reasons this data export will be available for you:\n\n{date}\n\nAt this time please try again."; // Wnd specific diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index 3f77507615a84d..48e6a2e7778fef 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -1023,6 +1023,7 @@ invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector query:!X = X; initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X; invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X; +invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X; invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode; @@ -1082,6 +1083,7 @@ account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_co account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode; account.verifyEmail#ecba39db email:string code:string = Bool; account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout; +account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1200,6 +1202,7 @@ messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = message messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector = Updates; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets; +messages.getSplitRanges#1cff7e08 = Vector; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 990d829f19ee92..9eab477c7d58e9 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -77,7 +77,7 @@ void Session::startExport() { _exportViewChanges.fire(_exportPanel.get()); - _exportPanel->closed( + _exportPanel->stopRequests( ) | rpl::start_with_next([=] { stopExport(); }, _export->lifetime()); diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index 5a7984045bd90c..c17c900f080ff8 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -707,6 +707,23 @@ void ApiWrap::requestMessages( }); } +void ApiWrap::finishExport(FnMut done) { + const auto guard = gsl::finally([&] { _takeoutId = base::none; }); + + mainRequest(MTPaccount_FinishTakeoutSession( + MTP_flags(MTPaccount_FinishTakeoutSession::Flag::f_success) + )).done(std::move(done)).send(); +} + +void ApiWrap::cancelExportFast() { + if (_takeoutId.has_value()) { + const auto requestId = mainRequest(MTPaccount_FinishTakeoutSession( + MTP_flags(0) + )).send(); + _mtp.request(requestId).detach(); + } +} + void ApiWrap::requestDialogsSlice() { Expects(_dialogsProcess != nullptr); diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h index 376fe143ba0d05..4dc7694197c2ba 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.h +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -81,6 +81,9 @@ class ApiWrap { Fn slice, FnMut done); + void finishExport(FnMut done); + void cancelExportFast(); + ~ApiWrap(); private: diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp index 89bfcfa06afeb2..6992692d384691 100644 --- a/Telegram/SourceFiles/export/export_controller.cpp +++ b/Telegram/SourceFiles/export/export_controller.cpp @@ -33,6 +33,7 @@ class Controller { // Processing step. void startExport(const Settings &settings); + void cancelExportFast(); private: using Step = ProcessingState::Step; @@ -153,6 +154,9 @@ rpl::producer Controller::state() const { } void Controller::setState(State &&state) { + if (_state.is()) { + return; + } _state = std::move(state); _stateChanges.fire_copy(_state); } @@ -326,6 +330,11 @@ void Controller::fillSubstepsInSteps(const ApiWrap::StartInfo &info) { _substepsTotal = ranges::accumulate(_substepsInStep, 0); } +void Controller::cancelExportFast() { + _api.cancelExportFast(); + setState(CancelledState()); +} + void Controller::exportNext() { if (!++_stepIndex) { if (ioCatchError(_writer->start(_settings, &_stats))) { @@ -336,9 +345,12 @@ void Controller::exportNext() { if (ioCatchError(_writer->finish())) { return; } - setFinishedState(); + _api.finishExport([=] { + setFinishedState(); + }); return; } + const auto step = _steps[_stepIndex]; switch (step) { case Step::Initializing: return initialize(); @@ -713,6 +725,14 @@ void ControllerWrap::startExport(const Settings &settings) { }); } +void ControllerWrap::cancelExportFast() { + LOG(("Export Info: Cancelled export.")); + + _wrapped.with([=](Controller &controller) { + controller.cancelExportFast(); + }); +} + rpl::lifetime &ControllerWrap::lifetime() { return _lifetime; } diff --git a/Telegram/SourceFiles/export/export_controller.h b/Telegram/SourceFiles/export/export_controller.h index 8baf3202e4aed4..9d38f1882d3472 100644 --- a/Telegram/SourceFiles/export/export_controller.h +++ b/Telegram/SourceFiles/export/export_controller.h @@ -74,6 +74,9 @@ struct OutputErrorState { QString path; }; +struct CancelledState { +}; + struct FinishedState { QString path; int filesCount = 0; @@ -85,6 +88,7 @@ using State = base::optional_variant< ProcessingState, ApiErrorState, OutputErrorState, + CancelledState, FinishedState>; //struct PasswordUpdate { @@ -113,6 +117,7 @@ class ControllerWrap { // Processing step. void startExport(const Settings &settings); + void cancelExportFast(); rpl::lifetime &lifetime(); diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp index b578af10c39f21..551f60d0f16a68 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp @@ -20,6 +20,11 @@ For license and copyright information please follow this link: namespace Export { namespace View { +namespace { + +constexpr auto kAddDelay = TimeId(60); + +} // namespace PanelController::PanelController(not_null process) : _process(process) { @@ -64,17 +69,27 @@ void PanelController::showSettings() { } void PanelController::showError(const ApiErrorState &error) { - showError("API Error happened :(\n" - + QString::number(error.data.code()) + ": " + error.data.type() - + "\n" + error.data.description()); + if (error.data.type() == qstr("TAKEOUT_INVALID")) { + showError(lang(lng_export_invalid)); + } else if (error.data.type().startsWith(qstr("TAKEOUT_INIT_DELAY_"))) { + const auto seconds = std::max(error.data.type().mid( + qstr("TAKEOUT_INIT_DELAY_").size()).toInt(), 0) + kAddDelay; + const auto now = QDateTime::currentDateTime(); + const auto when = now.addSecs(seconds); + showError(lng_export_delay(lt_date, langDateTimeFull(when))); + } else { + showCriticalError("API Error happened :(\n" + + QString::number(error.data.code()) + ": " + error.data.type() + + "\n" + error.data.description()); + } } void PanelController::showError(const OutputErrorState &error) { - showError("Disk Error happened :(\n" + showCriticalError("Disk Error happened :(\n" "Could not write path:\n" + error.path); } -void PanelController::showError(const QString &text) { +void PanelController::showCriticalError(const QString &text) { auto container = base::make_unique_q>( _panel.get(), object_ptr( @@ -92,6 +107,26 @@ void PanelController::showError(const QString &text) { _panel->setHideOnDeactivate(false); } +void PanelController::showError(const QString &text) { + auto box = Box(text); + const auto weak = make_weak(box.data()); + const auto hidden = _panel->isHidden(); + _panel->showBox( + std::move(box), + LayerOption::CloseOther, + hidden ? anim::type::instant : anim::type::normal); + weak->setCloseByEscape(false); + weak->setCloseByOutsideClick(false); + weak->boxClosing( + ) | rpl::start_with_next([=] { + _panel->hideGetDuration(); + }, weak->lifetime()); + if (hidden) { + _panel->showAndActivate(); + } + _panel->setHideOnDeactivate(false); +} + void PanelController::showProgress() { _panel->setTitle(Lang::Viewer(lng_export_progress_title)); @@ -125,10 +160,11 @@ void PanelController::stopWithConfirmation(FnMut callback) { return; } auto stop = [=, callback = std::move(callback)]() mutable { - auto saved = std::move(callback); - stopExport(); - if (saved) { + if (auto saved = std::move(callback)) { + stopExport(); saved(); + } else { + _process->cancelExportFast(); } }; const auto hidden = _panel->isHidden(); @@ -157,7 +193,7 @@ void PanelController::stopExport() { _panel->hideGetDuration(); } -rpl::producer<> PanelController::closed() const { +rpl::producer<> PanelController::stopRequests() const { return _panelCloseEvents.events( ) | rpl::flatten_latest( ) | rpl::filter([=] { @@ -174,9 +210,11 @@ void PanelController::updateState(State &&state) { showError(*apiError); } else if (const auto error = base::get_if(&_state)) { showError(*error); - } else if (const auto finished = base::get_if(&_state)) { + } else if (_state.is()) { _panel->setTitle(Lang::Viewer(lng_export_title)); _panel->setHideOnDeactivate(false); + } else if (_state.is()) { + stopExport(); } } diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.h b/Telegram/SourceFiles/export/view/export_view_panel_controller.h index 9b82af8a8c02cf..cd31890598dc70 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.h +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.h @@ -29,7 +29,7 @@ class PanelController { void activatePanel(); void stopWithConfirmation(FnMut callback = nullptr); - rpl::producer<> closed() const; + rpl::producer<> stopRequests() const; rpl::lifetime &lifetime() { return _lifetime; @@ -50,6 +50,7 @@ class PanelController { void showError(const ApiErrorState &error); void showError(const OutputErrorState &error); void showError(const QString &text); + void showCriticalError(const QString &text); not_null _process; diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.cpp b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp index 94652d716d1fd1..7230a260d39f8e 100644 --- a/Telegram/SourceFiles/mtproto/concurrent_sender.cpp +++ b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp @@ -198,7 +198,7 @@ void ConcurrentSender::senderRequestFail( } void ConcurrentSender::senderRequestCancel(mtpRequestId requestId) { - _requests.erase(requestId); + senderRequestDetach(requestId); with_instance([=](not_null instance) { instance->cancel(requestId); }); @@ -216,4 +216,8 @@ void ConcurrentSender::senderRequestCancelAll() { }); } +void ConcurrentSender::senderRequestDetach(mtpRequestId requestId) { + _requests.erase(requestId); +} + } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.h b/Telegram/SourceFiles/mtproto/concurrent_sender.h index 883076cb64c753..3afc11600a8923 100644 --- a/Telegram/SourceFiles/mtproto/concurrent_sender.h +++ b/Telegram/SourceFiles/mtproto/concurrent_sender.h @@ -148,6 +148,7 @@ class ConcurrentSender : public base::has_weak_ptr { class SentRequestWrap { public: void cancel(); + void detach(); private: friend class ConcurrentSender; @@ -190,6 +191,7 @@ class ConcurrentSender : public base::has_weak_ptr { RPCError &&error); void senderRequestCancel(mtpRequestId requestId); void senderRequestCancelAll(); + void senderRequestDetach(mtpRequestId requestId); const Fn)> _runner; base::flat_map _requests; @@ -446,6 +448,10 @@ inline void ConcurrentSender::SentRequestWrap::cancel() { _sender->senderRequestCancel(_requestId); } +inline void ConcurrentSender::SentRequestWrap::detach() { + _sender->senderRequestDetach(_requestId); +} + inline ConcurrentSender::SentRequestWrap::SentRequestWrap( not_null sender, mtpRequestId requestId