From e8dd277a00431b6a4c0547896bbb8abaf30d5704 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 20 Jun 2018 16:54:13 +0100 Subject: [PATCH] Improve export progress / finished design. --- Telegram/Resources/langs/lang.strings | 8 +++ .../SourceFiles/export/export_controller.h | 7 +- .../export/output/export_output_text.cpp | 2 +- Telegram/SourceFiles/export/view/export.style | 20 ++++++ .../export/view/export_view_content.cpp | 25 +++++++ .../export/view/export_view_content.h | 7 +- .../view/export_view_panel_controller.cpp | 35 +++++++++- .../view/export_view_panel_controller.h | 3 + .../export/view/export_view_progress.cpp | 70 +++++++++++-------- .../export/view/export_view_progress.h | 10 ++- Telegram/SourceFiles/mainwidget.cpp | 3 +- 11 files changed, 149 insertions(+), 41 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4ab96e951c..e11fcdf2b0 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1687,6 +1687,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_export_state_video_message" = "Round video message"; "lng_export_state_sticker" = "Sticker"; "lng_export_state_gif" = "Animated GIF"; +"lng_export_progress" = "Note: Please don't close Telegram while exporting files and personal data."; +"lng_export_stop" = "Stop"; +"lng_export_sure_stop" = "Are you sure you want to stop exporting your data?\n\nThis action cannot be undone."; +"lng_export_about_done" = "Your data is successfully exported."; +"lng_export_done" = "Show my data"; +"lng_export_finished" = "Export is finished."; +"lng_export_total_files" = "Total files: {count}"; +"lng_export_total_size" = "Total size: {size}"; // Wnd specific diff --git a/Telegram/SourceFiles/export/export_controller.h b/Telegram/SourceFiles/export/export_controller.h index 4b97ef7d8e..f53b12c2d3 100644 --- a/Telegram/SourceFiles/export/export_controller.h +++ b/Telegram/SourceFiles/export/export_controller.h @@ -22,7 +22,6 @@ struct PasswordCheckState { bool requesting = true; bool hasPassword = false; bool checked = false; - }; struct ProcessingState { @@ -65,22 +64,20 @@ struct ProcessingState { QString bytesName; int bytesLoaded = 0; int bytesCount = 0; - }; struct ApiErrorState { RPCError data; - }; struct OutputErrorState { QString path; - }; struct FinishedState { QString path; - + int filesCount = 0; + int64 bytesCount = 0; }; using State = base::optional_variant< diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index 89ae44d67d..f63d3c05e0 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -818,7 +818,7 @@ QString TextWriter::mainFilePath() { } QString TextWriter::mainFileRelativePath() const { - return "result.txt"; + return "overview.txt"; } QString TextWriter::pathWithRelativePath(const QString &path) const { diff --git a/Telegram/SourceFiles/export/view/export.style b/Telegram/SourceFiles/export/view/export.style index fe393f4a1d..3239dd7a17 100644 --- a/Telegram/SourceFiles/export/view/export.style +++ b/Telegram/SourceFiles/export/view/export.style @@ -58,11 +58,31 @@ exportProgressLabel: FlatLabel(boxLabel) { exportProgressInfoLabel: FlatLabel(boxLabel) { textFg: windowSubTextFg; maxHeight: 20px; + style: boxTextStyle; } exportProgressWidth: 3px; exportProgressFg: mediaPlayerActiveFg; exportProgressBg: mediaPlayerInactiveFg; +exportCancelButton: RoundButton(attentionBoxButton) { + width: 200px; + height: 44px; + textTop: 12px; + font: font(semibold 15px); +} +exportCancelBottom: 30px; +exportDoneButton: RoundButton(defaultActiveButton) { + width: 200px; + height: 44px; + textTop: 12px; + font: font(semibold 15px); +} + +exportAboutLabel: FlatLabel(boxLabel) { + textFg: windowSubTextFg; +} +exportAboutPadding: margins(22px, 10px, 22px, 0px); + exportTopBarLabel: FlatLabel(defaultFlatLabel) { maxHeight: 20px; palette: TextPalette(defaultTextPalette) { diff --git a/Telegram/SourceFiles/export/view/export_view_content.cpp b/Telegram/SourceFiles/export/view/export_view_content.cpp index f517cf5453..60fcda275a 100644 --- a/Telegram/SourceFiles/export/view/export_view_content.cpp +++ b/Telegram/SourceFiles/export/view/export_view_content.cpp @@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Export { namespace View { +const QString Content::kDoneId = "done"; + Content ContentFromState(const ProcessingState &state) { using Step = ProcessingState::Step; @@ -101,6 +103,29 @@ Content ContentFromState(const ProcessingState &state) { break; default: Unexpected("Step in ContentFromState."); } + while (result.rows.size() < 3) { + result.rows.push_back(Content::Row()); + } + return result; +} + +Content ContentFromState(const FinishedState &state) { + auto result = Content(); + result.rows.push_back({ + Content::kDoneId, + lang(lng_export_finished), + QString(), + 1. }); + result.rows.push_back({ + Content::kDoneId, + lng_export_total_files(lt_count, QString::number(state.filesCount)), + QString(), + 1. }); + result.rows.push_back({ + Content::kDoneId, + lng_export_total_size(lt_size, formatSizeText(state.bytesCount)), + QString(), + 1. }); return result; } diff --git a/Telegram/SourceFiles/export/view/export_view_content.h b/Telegram/SourceFiles/export/view/export_view_content.h index 0240f8b398..b31e64a7b2 100644 --- a/Telegram/SourceFiles/export/view/export_view_content.h +++ b/Telegram/SourceFiles/export/view/export_view_content.h @@ -22,9 +22,12 @@ struct Content { std::vector rows; + static const QString kDoneId; + }; Content ContentFromState(const ProcessingState &state); +Content ContentFromState(const FinishedState &state); inline auto ContentFromState(rpl::producer state) { return std::move( @@ -34,8 +37,10 @@ inline auto ContentFromState(rpl::producer state) { }) | rpl::map([](const State &state) { if (const auto process = base::get_if(&state)) { return ContentFromState(*process); + } else if (const auto done = base::get_if(&state)) { + return ContentFromState(*done); } - return Content(); + Unexpected("State type in ContentFromState."); }); } diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp index aae8d4dd89..c62393c6f1 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp @@ -13,9 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/separate_panel.h" #include "ui/wrap/padding_wrap.h" +#include "boxes/confirm_box.h" #include "lang/lang_keys.h" #include "core/file_utilities.h" #include "styles/style_export.h" +#include "styles/style_boxes.h" namespace Export { namespace View { @@ -102,13 +104,39 @@ void PanelController::showProgress() { progress->cancelClicks( ) | rpl::start_with_next([=] { - _panel->hideGetDuration(); + stopWithConfirmation(); + }, progress->lifetime()); + + progress->doneClicks( + ) | rpl::start_with_next([=] { + if (const auto finished = base::get_if(&_state)) { + File::ShowInFolder(finished->path); + _panel->hideGetDuration(); + } }, progress->lifetime()); _panel->showInner(std::move(progress)); _panel->setHideOnDeactivate(true); } +void PanelController::stopWithConfirmation() { + auto box = Box( + lang(lng_export_sure_stop), + lang(lng_export_stop), + st::attentionBoxButton, + [=] { stopExport(); }); + _panel->showBox( + std::move(box), + LayerOption::KeepOther, + anim::type::normal); +} + +void PanelController::stopExport() { + _stopRequested = true; + _panel->showAndActivate(); + _panel->hideGetDuration(); +} + void PanelController::showDone(const QString &path) { _panel->setTitle(Lang::Viewer(lng_export_title)); @@ -133,7 +161,7 @@ rpl::producer<> PanelController::closed() const { return _panelCloseEvents.events( ) | rpl::flatten_latest( ) | rpl::filter([=] { - return !_state.is(); + return !_state.is() || _stopRequested; }); } @@ -147,7 +175,8 @@ void PanelController::updateState(State &&state) { } else if (const auto error = base::get_if(&_state)) { showError(*error); } else if (const auto finished = base::get_if(&_state)) { - showDone(finished->path); + _panel->setHideOnDeactivate(false); + // showDone(finished->path); } } diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.h b/Telegram/SourceFiles/export/view/export_view_panel_controller.h index a273aab3cd..63abbdfd98 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.h +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.h @@ -25,6 +25,7 @@ public: PanelController(not_null process); void activatePanel(); + void stopWithConfirmation(); rpl::producer<> closed() const; @@ -39,6 +40,7 @@ public: ~PanelController(); private: + void stopExport(); void createPanel(); void updateState(State &&state); void showSettings(); @@ -54,6 +56,7 @@ private: State _state; rpl::event_stream> _panelCloseEvents; + bool _stopRequested = false; rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/export/view/export_view_progress.cpp b/Telegram/SourceFiles/export/view/export_view_progress.cpp index e43fa650b7..437601c9dd 100644 --- a/Telegram/SourceFiles/export/view/export_view_progress.cpp +++ b/Telegram/SourceFiles/export/view/export_view_progress.cpp @@ -237,61 +237,64 @@ ProgressWidget::ProgressWidget( rpl::producer content) : RpWidget(parent) , _body(this) { - initFooter(); - widthValue( ) | rpl::start_with_next([=](int width) { _body->resizeToWidth(width); _body->moveToLeft(0, 0); }, _body->lifetime()); + _about = _body->add( + object_ptr( + this, + lang(lng_export_progress), + Ui::FlatLabel::InitType::Simple, + st::exportAboutLabel), + st::exportAboutPadding); + std::move( content ) | rpl::start_with_next([=](Content &&content) { updateState(std::move(content)); }, lifetime()); + + _cancel = base::make_unique_q( + this, + langFactory(lng_export_stop), + st::exportCancelButton); + setupBottomButton(_cancel.get()); } rpl::producer<> ProgressWidget::cancelClicks() const { - return _cancel->clicks(); + return _cancel ? _cancel->clicks() : rpl::never<>(); } -void ProgressWidget::initFooter() { - const auto buttonsPadding = st::boxButtonPadding; - const auto buttonsHeight = buttonsPadding.top() - + st::defaultBoxButton.height - + buttonsPadding.bottom(); - const auto buttons = Ui::CreateChild( - this, - buttonsHeight); +rpl::producer<> ProgressWidget::doneClicks() const { + return _doneClicks.events(); +} + +void ProgressWidget::setupBottomButton(not_null button) { + button->show(); sizeValue( ) | rpl::start_with_next([=](QSize size) { - buttons->resizeToWidth(size.width()); - buttons->moveToLeft(0, size.height() - buttons->height()); - }, lifetime()); - - _cancel = Ui::CreateChild( - buttons, - langFactory(lng_cancel), - st::defaultBoxButton); - _cancel->show(); - - buttons->widthValue( - ) | rpl::start_with_next([=] { - const auto right = st::boxButtonPadding.right(); - const auto top = st::boxButtonPadding.top(); - _cancel->moveToRight(right, top); - }, _cancel->lifetime()); + button->move( + (size.width() - button->width()) / 2, + (size.height() - st::exportCancelBottom - button->height())); + }, button->lifetime()); } void ProgressWidget::updateState(Content &&content) { + if (!content.rows.empty() && content.rows[0].id == Content::kDoneId) { + showDone(); + } + auto index = 0; for (auto &row : content.rows) { if (index < _rows.size()) { _rows[index]->updateData(std::move(row)); } else { - _rows.push_back(_body->add( + _rows.push_back(_body->insert( + index, object_ptr(this, std::move(row)), st::exportProgressRowPadding)); } @@ -302,6 +305,17 @@ void ProgressWidget::updateState(Content &&content) { } } +void ProgressWidget::showDone() { + _cancel = nullptr; + _about->setText(lang(lng_export_about_done)); + _done = base::make_unique_q( + this, + langFactory(lng_export_done), + st::exportDoneButton); + _done->clicks() | rpl::start_to_stream(_doneClicks, _done->lifetime()); + setupBottomButton(_done.get()); +} + ProgressWidget::~ProgressWidget() = default; } // namespace View diff --git a/Telegram/SourceFiles/export/view/export_view_progress.h b/Telegram/SourceFiles/export/view/export_view_progress.h index 252f46238d..e792a2165e 100644 --- a/Telegram/SourceFiles/export/view/export_view_progress.h +++ b/Telegram/SourceFiles/export/view/export_view_progress.h @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { class VerticalLayout; class RoundButton; +class FlatLabel; } // namespace Ui namespace Export { @@ -25,12 +26,14 @@ public: rpl::producer content); rpl::producer<> cancelClicks() const; + rpl::producer<> doneClicks() const; ~ProgressWidget(); private: - void initFooter(); + void setupBottomButton(not_null button); void updateState(Content &&content); + void showDone(); Content _content; @@ -38,7 +41,10 @@ private: object_ptr _body; std::vector> _rows; - QPointer _cancel; + QPointer _about; + base::unique_qptr _cancel; + base::unique_qptr _done; + rpl::event_stream<> _doneClicks; }; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 499d2b8119..681c6444f7 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1693,7 +1693,8 @@ void MainWidget::setCurrentExportView(Export::View::PanelController *view) { if (_currentExportView) { _currentExportView->progressState( ) | rpl::start_with_next([=](Export::View::Content &&data) { - if (data.rows.empty()) { + if (!data.rows.empty() + && data.rows[0].id == Export::View::Content::kDoneId) { destroyExportTopBar(); } else if (!_exportTopBar) { createExportTopBar(std::move(data));