mirror of https://github.com/procxx/kepka.git
Improve export progress / finished design.
This commit is contained in:
parent
329db0d8e9
commit
e8dd277a00
|
@ -1687,6 +1687,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_export_state_video_message" = "Round video message";
|
"lng_export_state_video_message" = "Round video message";
|
||||||
"lng_export_state_sticker" = "Sticker";
|
"lng_export_state_sticker" = "Sticker";
|
||||||
"lng_export_state_gif" = "Animated GIF";
|
"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
|
// Wnd specific
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ struct PasswordCheckState {
|
||||||
bool requesting = true;
|
bool requesting = true;
|
||||||
bool hasPassword = false;
|
bool hasPassword = false;
|
||||||
bool checked = false;
|
bool checked = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProcessingState {
|
struct ProcessingState {
|
||||||
|
@ -65,22 +64,20 @@ struct ProcessingState {
|
||||||
QString bytesName;
|
QString bytesName;
|
||||||
int bytesLoaded = 0;
|
int bytesLoaded = 0;
|
||||||
int bytesCount = 0;
|
int bytesCount = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ApiErrorState {
|
struct ApiErrorState {
|
||||||
RPCError data;
|
RPCError data;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OutputErrorState {
|
struct OutputErrorState {
|
||||||
QString path;
|
QString path;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FinishedState {
|
struct FinishedState {
|
||||||
QString path;
|
QString path;
|
||||||
|
int filesCount = 0;
|
||||||
|
int64 bytesCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
using State = base::optional_variant<
|
using State = base::optional_variant<
|
||||||
|
|
|
@ -818,7 +818,7 @@ QString TextWriter::mainFilePath() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextWriter::mainFileRelativePath() const {
|
QString TextWriter::mainFileRelativePath() const {
|
||||||
return "result.txt";
|
return "overview.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextWriter::pathWithRelativePath(const QString &path) const {
|
QString TextWriter::pathWithRelativePath(const QString &path) const {
|
||||||
|
|
|
@ -58,11 +58,31 @@ exportProgressLabel: FlatLabel(boxLabel) {
|
||||||
exportProgressInfoLabel: FlatLabel(boxLabel) {
|
exportProgressInfoLabel: FlatLabel(boxLabel) {
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
|
style: boxTextStyle;
|
||||||
}
|
}
|
||||||
exportProgressWidth: 3px;
|
exportProgressWidth: 3px;
|
||||||
exportProgressFg: mediaPlayerActiveFg;
|
exportProgressFg: mediaPlayerActiveFg;
|
||||||
exportProgressBg: mediaPlayerInactiveFg;
|
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) {
|
exportTopBarLabel: FlatLabel(defaultFlatLabel) {
|
||||||
maxHeight: 20px;
|
maxHeight: 20px;
|
||||||
palette: TextPalette(defaultTextPalette) {
|
palette: TextPalette(defaultTextPalette) {
|
||||||
|
|
|
@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
|
||||||
|
const QString Content::kDoneId = "done";
|
||||||
|
|
||||||
Content ContentFromState(const ProcessingState &state) {
|
Content ContentFromState(const ProcessingState &state) {
|
||||||
using Step = ProcessingState::Step;
|
using Step = ProcessingState::Step;
|
||||||
|
|
||||||
|
@ -101,6 +103,29 @@ Content ContentFromState(const ProcessingState &state) {
|
||||||
break;
|
break;
|
||||||
default: Unexpected("Step in ContentFromState.");
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,12 @@ struct Content {
|
||||||
|
|
||||||
std::vector<Row> rows;
|
std::vector<Row> rows;
|
||||||
|
|
||||||
|
static const QString kDoneId;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Content ContentFromState(const ProcessingState &state);
|
Content ContentFromState(const ProcessingState &state);
|
||||||
|
Content ContentFromState(const FinishedState &state);
|
||||||
|
|
||||||
inline auto ContentFromState(rpl::producer<State> state) {
|
inline auto ContentFromState(rpl::producer<State> state) {
|
||||||
return std::move(
|
return std::move(
|
||||||
|
@ -34,8 +37,10 @@ inline auto ContentFromState(rpl::producer<State> state) {
|
||||||
}) | rpl::map([](const State &state) {
|
}) | rpl::map([](const State &state) {
|
||||||
if (const auto process = base::get_if<ProcessingState>(&state)) {
|
if (const auto process = base::get_if<ProcessingState>(&state)) {
|
||||||
return ContentFromState(*process);
|
return ContentFromState(*process);
|
||||||
|
} else if (const auto done = base::get_if<FinishedState>(&state)) {
|
||||||
|
return ContentFromState(*done);
|
||||||
}
|
}
|
||||||
return Content();
|
Unexpected("State type in ContentFromState.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/separate_panel.h"
|
#include "ui/widgets/separate_panel.h"
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
||||||
|
#include "boxes/confirm_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
#include "styles/style_export.h"
|
#include "styles/style_export.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace View {
|
namespace View {
|
||||||
|
@ -102,13 +104,39 @@ void PanelController::showProgress() {
|
||||||
|
|
||||||
progress->cancelClicks(
|
progress->cancelClicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
|
stopWithConfirmation();
|
||||||
|
}, progress->lifetime());
|
||||||
|
|
||||||
|
progress->doneClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (const auto finished = base::get_if<FinishedState>(&_state)) {
|
||||||
|
File::ShowInFolder(finished->path);
|
||||||
_panel->hideGetDuration();
|
_panel->hideGetDuration();
|
||||||
|
}
|
||||||
}, progress->lifetime());
|
}, progress->lifetime());
|
||||||
|
|
||||||
_panel->showInner(std::move(progress));
|
_panel->showInner(std::move(progress));
|
||||||
_panel->setHideOnDeactivate(true);
|
_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) {
|
void PanelController::showDone(const QString &path) {
|
||||||
_panel->setTitle(Lang::Viewer(lng_export_title));
|
_panel->setTitle(Lang::Viewer(lng_export_title));
|
||||||
|
|
||||||
|
@ -133,7 +161,7 @@ rpl::producer<> PanelController::closed() const {
|
||||||
return _panelCloseEvents.events(
|
return _panelCloseEvents.events(
|
||||||
) | rpl::flatten_latest(
|
) | rpl::flatten_latest(
|
||||||
) | rpl::filter([=] {
|
) | 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)) {
|
} else if (const auto error = base::get_if<OutputErrorState>(&_state)) {
|
||||||
showError(*error);
|
showError(*error);
|
||||||
} else if (const auto finished = base::get_if<FinishedState>(&_state)) {
|
} else if (const auto finished = base::get_if<FinishedState>(&_state)) {
|
||||||
showDone(finished->path);
|
_panel->setHideOnDeactivate(false);
|
||||||
|
// showDone(finished->path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
PanelController(not_null<ControllerWrap*> process);
|
PanelController(not_null<ControllerWrap*> process);
|
||||||
|
|
||||||
void activatePanel();
|
void activatePanel();
|
||||||
|
void stopWithConfirmation();
|
||||||
|
|
||||||
rpl::producer<> closed() const;
|
rpl::producer<> closed() const;
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ public:
|
||||||
~PanelController();
|
~PanelController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void stopExport();
|
||||||
void createPanel();
|
void createPanel();
|
||||||
void updateState(State &&state);
|
void updateState(State &&state);
|
||||||
void showSettings();
|
void showSettings();
|
||||||
|
@ -54,6 +56,7 @@ private:
|
||||||
|
|
||||||
State _state;
|
State _state;
|
||||||
rpl::event_stream<rpl::producer<>> _panelCloseEvents;
|
rpl::event_stream<rpl::producer<>> _panelCloseEvents;
|
||||||
|
bool _stopRequested = false;
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -237,61 +237,64 @@ ProgressWidget::ProgressWidget(
|
||||||
rpl::producer<Content> content)
|
rpl::producer<Content> content)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _body(this) {
|
, _body(this) {
|
||||||
initFooter();
|
|
||||||
|
|
||||||
widthValue(
|
widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
_body->resizeToWidth(width);
|
_body->resizeToWidth(width);
|
||||||
_body->moveToLeft(0, 0);
|
_body->moveToLeft(0, 0);
|
||||||
}, _body->lifetime());
|
}, _body->lifetime());
|
||||||
|
|
||||||
|
_about = _body->add(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
this,
|
||||||
|
lang(lng_export_progress),
|
||||||
|
Ui::FlatLabel::InitType::Simple,
|
||||||
|
st::exportAboutLabel),
|
||||||
|
st::exportAboutPadding);
|
||||||
|
|
||||||
std::move(
|
std::move(
|
||||||
content
|
content
|
||||||
) | rpl::start_with_next([=](Content &&content) {
|
) | rpl::start_with_next([=](Content &&content) {
|
||||||
updateState(std::move(content));
|
updateState(std::move(content));
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_cancel = base::make_unique_q<Ui::RoundButton>(
|
||||||
|
this,
|
||||||
|
langFactory(lng_export_stop),
|
||||||
|
st::exportCancelButton);
|
||||||
|
setupBottomButton(_cancel.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> ProgressWidget::cancelClicks() const {
|
rpl::producer<> ProgressWidget::cancelClicks() const {
|
||||||
return _cancel->clicks();
|
return _cancel ? _cancel->clicks() : rpl::never<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressWidget::initFooter() {
|
rpl::producer<> ProgressWidget::doneClicks() const {
|
||||||
const auto buttonsPadding = st::boxButtonPadding;
|
return _doneClicks.events();
|
||||||
const auto buttonsHeight = buttonsPadding.top()
|
}
|
||||||
+ st::defaultBoxButton.height
|
|
||||||
+ buttonsPadding.bottom();
|
void ProgressWidget::setupBottomButton(not_null<Ui::RoundButton*> button) {
|
||||||
const auto buttons = Ui::CreateChild<Ui::FixedHeightWidget>(
|
button->show();
|
||||||
this,
|
|
||||||
buttonsHeight);
|
|
||||||
|
|
||||||
sizeValue(
|
sizeValue(
|
||||||
) | rpl::start_with_next([=](QSize size) {
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
buttons->resizeToWidth(size.width());
|
button->move(
|
||||||
buttons->moveToLeft(0, size.height() - buttons->height());
|
(size.width() - button->width()) / 2,
|
||||||
}, lifetime());
|
(size.height() - st::exportCancelBottom - button->height()));
|
||||||
|
}, button->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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressWidget::updateState(Content &&content) {
|
void ProgressWidget::updateState(Content &&content) {
|
||||||
|
if (!content.rows.empty() && content.rows[0].id == Content::kDoneId) {
|
||||||
|
showDone();
|
||||||
|
}
|
||||||
|
|
||||||
auto index = 0;
|
auto index = 0;
|
||||||
for (auto &row : content.rows) {
|
for (auto &row : content.rows) {
|
||||||
if (index < _rows.size()) {
|
if (index < _rows.size()) {
|
||||||
_rows[index]->updateData(std::move(row));
|
_rows[index]->updateData(std::move(row));
|
||||||
} else {
|
} else {
|
||||||
_rows.push_back(_body->add(
|
_rows.push_back(_body->insert(
|
||||||
|
index,
|
||||||
object_ptr<Row>(this, std::move(row)),
|
object_ptr<Row>(this, std::move(row)),
|
||||||
st::exportProgressRowPadding));
|
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;
|
ProgressWidget::~ProgressWidget() = default;
|
||||||
|
|
||||||
} // namespace View
|
} // namespace View
|
||||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
class RoundButton;
|
class RoundButton;
|
||||||
|
class FlatLabel;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
|
@ -25,12 +26,14 @@ public:
|
||||||
rpl::producer<Content> content);
|
rpl::producer<Content> content);
|
||||||
|
|
||||||
rpl::producer<> cancelClicks() const;
|
rpl::producer<> cancelClicks() const;
|
||||||
|
rpl::producer<> doneClicks() const;
|
||||||
|
|
||||||
~ProgressWidget();
|
~ProgressWidget();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initFooter();
|
void setupBottomButton(not_null<Ui::RoundButton*> button);
|
||||||
void updateState(Content &&content);
|
void updateState(Content &&content);
|
||||||
|
void showDone();
|
||||||
|
|
||||||
Content _content;
|
Content _content;
|
||||||
|
|
||||||
|
@ -38,7 +41,10 @@ private:
|
||||||
object_ptr<Ui::VerticalLayout> _body;
|
object_ptr<Ui::VerticalLayout> _body;
|
||||||
std::vector<not_null<Row*>> _rows;
|
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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1693,7 +1693,8 @@ void MainWidget::setCurrentExportView(Export::View::PanelController *view) {
|
||||||
if (_currentExportView) {
|
if (_currentExportView) {
|
||||||
_currentExportView->progressState(
|
_currentExportView->progressState(
|
||||||
) | rpl::start_with_next([=](Export::View::Content &&data) {
|
) | 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();
|
destroyExportTopBar();
|
||||||
} else if (!_exportTopBar) {
|
} else if (!_exportTopBar) {
|
||||||
createExportTopBar(std::move(data));
|
createExportTopBar(std::move(data));
|
||||||
|
|
Loading…
Reference in New Issue