Improve export progress / finished design.

This commit is contained in:
John Preston 2018-06-20 16:54:13 +01:00
parent 329db0d8e9
commit e8dd277a00
11 changed files with 149 additions and 41 deletions

View File

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

View File

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

View File

@ -818,7 +818,7 @@ QString TextWriter::mainFilePath() {
}
QString TextWriter::mainFileRelativePath() const {
return "result.txt";
return "overview.txt";
}
QString TextWriter::pathWithRelativePath(const QString &path) const {

View File

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

View File

@ -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;
}

View File

@ -22,9 +22,12 @@ struct Content {
std::vector<Row> rows;
static const QString kDoneId;
};
Content ContentFromState(const ProcessingState &state);
Content ContentFromState(const FinishedState &state);
inline auto ContentFromState(rpl::producer<State> state) {
return std::move(
@ -34,8 +37,10 @@ inline auto ContentFromState(rpl::producer<State> state) {
}) | rpl::map([](const State &state) {
if (const auto process = base::get_if<ProcessingState>(&state)) {
return ContentFromState(*process);
} else if (const auto done = base::get_if<FinishedState>(&state)) {
return ContentFromState(*done);
}
return Content();
Unexpected("State type in ContentFromState.");
});
}

View File

@ -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<FinishedState>(&_state)) {
File::ShowInFolder(finished->path);
_panel->hideGetDuration();
}
}, progress->lifetime());
_panel->showInner(std::move(progress));
_panel->setHideOnDeactivate(true);
}
void PanelController::stopWithConfirmation() {
auto box = Box<ConfirmBox>(
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<ProcessingState>();
return !_state.is<ProcessingState>() || _stopRequested;
});
}
@ -147,7 +175,8 @@ void PanelController::updateState(State &&state) {
} else if (const auto error = base::get_if<OutputErrorState>(&_state)) {
showError(*error);
} else if (const auto finished = base::get_if<FinishedState>(&_state)) {
showDone(finished->path);
_panel->setHideOnDeactivate(false);
// showDone(finished->path);
}
}

View File

@ -25,6 +25,7 @@ public:
PanelController(not_null<ControllerWrap*> 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<rpl::producer<>> _panelCloseEvents;
bool _stopRequested = false;
rpl::lifetime _lifetime;
};

View File

@ -237,61 +237,64 @@ ProgressWidget::ProgressWidget(
rpl::producer<Content> 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<Ui::FlatLabel>(
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<Ui::RoundButton>(
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<Ui::FixedHeightWidget>(
this,
buttonsHeight);
rpl::producer<> ProgressWidget::doneClicks() const {
return _doneClicks.events();
}
void ProgressWidget::setupBottomButton(not_null<Ui::RoundButton*> 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<Ui::RoundButton>(
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<Row>(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<Ui::RoundButton>(
this,
langFactory(lng_export_done),
st::exportDoneButton);
_done->clicks() | rpl::start_to_stream(_doneClicks, _done->lifetime());
setupBottomButton(_done.get());
}
ProgressWidget::~ProgressWidget() = default;
} // namespace View

View File

@ -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> content);
rpl::producer<> cancelClicks() const;
rpl::producer<> doneClicks() const;
~ProgressWidget();
private:
void initFooter();
void setupBottomButton(not_null<Ui::RoundButton*> button);
void updateState(Content &&content);
void showDone();
Content _content;
@ -38,7 +41,10 @@ private:
object_ptr<Ui::VerticalLayout> _body;
std::vector<not_null<Row*>> _rows;
QPointer<Ui::RoundButton> _cancel;
QPointer<Ui::FlatLabel> _about;
base::unique_qptr<Ui::RoundButton> _cancel;
base::unique_qptr<Ui::RoundButton> _done;
rpl::event_stream<> _doneClicks;
};

View File

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