| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "boxes/local_storage_box.h"
- #include "ui/wrap/vertical_layout.h"
- #include "ui/wrap/slide_wrap.h"
- #include "ui/widgets/labels.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/shadow.h"
- #include "ui/widgets/continuous_sliders.h"
- #include "ui/effects/radial_animation.h"
- #include "ui/text/format_values.h"
- #include "ui/emoji_config.h"
- #include "storage/storage_account.h"
- #include "storage/cache/storage_cache_database.h"
- #include "data/data_session.h"
- #include "lang/lang_keys.h"
- #include "main/main_session.h"
- #include "window/window_session_controller.h"
- #include "styles/style_layers.h"
- #include "styles/style_boxes.h"
- namespace {
- constexpr auto kMegabyte = int64(1024 * 1024);
- constexpr auto kTotalSizeLimitsCount = 18;
- constexpr auto kMediaSizeLimitsCount = 18;
- constexpr auto kMinimalSizeLimit = 100 * kMegabyte;
- constexpr auto kTimeLimitsCount = 16;
- constexpr auto kMaxTimeLimitValue = std::numeric_limits<size_type>::max();
- constexpr auto kFakeMediaCacheTag = uint16(0xFFFF);
- int64 TotalSizeLimitInMB(int index) {
- if (index < 8) {
- return int64(index + 2) * 100;
- }
- return int64(index - 7) * 1024;
- }
- int64 TotalSizeLimit(int index) {
- return TotalSizeLimitInMB(index) * kMegabyte;
- }
- int64 MediaSizeLimitInMB(int index) {
- if (index < 9) {
- return int64(index + 1) * 100;
- }
- return int64(index - 8) * 1024;
- }
- int64 MediaSizeLimit(int index) {
- return MediaSizeLimitInMB(index) * kMegabyte;
- }
- QString SizeLimitText(int64 limit) {
- const auto mb = (limit / (1024 * 1024));
- const auto gb = (mb / 1024);
- return (gb > 0)
- ? (QString::number(gb) + " GB")
- : (QString::number(mb) + " MB");
- }
- size_type TimeLimitInDays(int index) {
- if (index < 3) {
- const auto weeks = (index + 1);
- return size_type(weeks) * 7;
- } else if (index < 15) {
- const auto month = (index - 2);
- return (size_type(month) * 30)
- + ((month >= 12) ? 5 :
- (month >= 10) ? 4 :
- (month >= 8) ? 3 :
- (month >= 7) ? 2 :
- (month >= 5) ? 1 :
- (month >= 3) ? 0 :
- (month >= 2) ? -1 :
- (month >= 1) ? 1 : 0);
- //+ (month >= 1 ? 1 : 0)
- //- (month >= 2 ? 2 : 0)
- //+ (month >= 3 ? 1 : 0)
- //+ (month >= 5 ? 1 : 0)
- //+ (month >= 7 ? 1 : 0)
- //+ (month >= 8 ? 1 : 0)
- //+ (month >= 10 ? 1 : 0)
- //+ (month >= 12 ? 1 : 0);
- }
- return 0;
- }
- size_type TimeLimit(int index) {
- const auto days = TimeLimitInDays(index);
- return days
- ? (days * 24 * 60 * 60)
- : kMaxTimeLimitValue;
- }
- QString TimeLimitText(size_type limit) {
- const auto days = (limit / (24 * 60 * 60));
- const auto weeks = (days / 7);
- const auto months = (days / 29);
- return (months > 0)
- ? tr::lng_months(tr::now, lt_count, months)
- : (limit > 0)
- ? tr::lng_weeks(tr::now, lt_count, weeks)
- : tr::lng_local_storage_limit_never(tr::now);
- }
- size_type LimitToValue(size_type timeLimit) {
- return timeLimit ? timeLimit : kMaxTimeLimitValue;
- }
- size_type ValueToLimit(size_type timeLimit) {
- return (timeLimit != kMaxTimeLimitValue) ? timeLimit : 0;
- }
- } // namespace
- class LocalStorageBox::Row : public Ui::RpWidget {
- public:
- Row(
- QWidget *parent,
- Fn<QString(size_type)> title,
- rpl::producer<QString> clear,
- const Database::TaggedSummary &data);
- void update(const Database::TaggedSummary &data);
- void toggleProgress(bool shown);
- rpl::producer<> clearRequests() const;
- protected:
- int resizeGetHeight(int newWidth) override;
- void paintEvent(QPaintEvent *e) override;
- private:
- QString titleText(const Database::TaggedSummary &data) const;
- QString sizeText(const Database::TaggedSummary &data) const;
- void radialAnimationCallback();
- Fn<QString(size_type)> _titleFactory;
- object_ptr<Ui::FlatLabel> _title;
- object_ptr<Ui::FlatLabel> _description;
- object_ptr<Ui::FlatLabel> _clearing = { nullptr };
- object_ptr<Ui::RoundButton> _clear;
- std::unique_ptr<Ui::InfiniteRadialAnimation> _progress;
- };
- LocalStorageBox::Row::Row(
- QWidget *parent,
- Fn<QString(size_type)> title,
- rpl::producer<QString> clear,
- const Database::TaggedSummary &data)
- : RpWidget(parent)
- , _titleFactory(std::move(title))
- , _title(
- this,
- titleText(data),
- st::localStorageRowTitle)
- , _description(
- this,
- sizeText(data),
- st::localStorageRowSize)
- , _clear(this, std::move(clear), st::localStorageClear) {
- _clear->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
- _clear->setVisible(data.count != 0);
- }
- void LocalStorageBox::Row::update(const Database::TaggedSummary &data) {
- if (data.count != 0) {
- _title->setText(titleText(data));
- }
- _description->setText(sizeText(data));
- _clear->setVisible(data.count != 0);
- }
- void LocalStorageBox::Row::toggleProgress(bool shown) {
- if (!shown) {
- _progress = nullptr;
- _description->show();
- _clearing.destroy();
- } else if (!_progress) {
- _progress = std::make_unique<Ui::InfiniteRadialAnimation>(
- [=] { radialAnimationCallback(); },
- st::proxyCheckingAnimation);
- _progress->start();
- _clearing = object_ptr<Ui::FlatLabel>(
- this,
- tr::lng_local_storage_clearing(tr::now),
- st::localStorageRowSize);
- _clearing->show();
- _description->hide();
- resizeToWidth(width());
- RpWidget::update();
- }
- }
- void LocalStorageBox::Row::radialAnimationCallback() {
- if (!anim::Disabled()) {
- RpWidget::update();
- }
- }
- rpl::producer<> LocalStorageBox::Row::clearRequests() const {
- return _clear->clicks() | rpl::to_empty;
- }
- int LocalStorageBox::Row::resizeGetHeight(int newWidth) {
- const auto height = st::localStorageRowHeight;
- const auto padding = st::localStorageRowPadding;
- const auto available = newWidth - padding.left() - padding.right();
- _title->resizeToWidth(available);
- _description->resizeToWidth(available);
- _title->moveToLeft(padding.left(), padding.top(), newWidth);
- _description->moveToLeft(
- padding.left(),
- height - padding.bottom() - _description->height(),
- newWidth);
- if (_clearing) {
- const auto progressShift = st::proxyCheckingPosition.x()
- + st::proxyCheckingAnimation.size.width()
- + st::proxyCheckingSkip;
- _clearing->resizeToWidth(available - progressShift);
- _clearing->moveToLeft(
- padding.left(),// + progressShift,
- _description->y(),
- newWidth);
- }
- _clear->moveToRight(
- st::layerBox.buttonPadding.right(),
- (height - _clear->height()) / 2,
- newWidth);
- return height;
- }
- void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
- #if 0 // not used
- if (!_progress) {
- return;
- }
- auto p = QPainter(this);
- const auto padding = st::localStorageRowPadding;
- const auto height = st::localStorageRowHeight;
- const auto bottom = height - padding.bottom() - _description->height();
- _progress->draw(
- p,
- {
- st::proxyCheckingPosition.x() + padding.left(),
- st::proxyCheckingPosition.y() + bottom
- },
- width());
- #endif
- }
- QString LocalStorageBox::Row::titleText(const Database::TaggedSummary &data) const {
- return _titleFactory(data.count);
- }
- QString LocalStorageBox::Row::sizeText(const Database::TaggedSummary &data) const {
- return data.totalSize
- ? Ui::FormatSizeText(data.totalSize)
- : tr::lng_local_storage_empty(tr::now);
- }
- LocalStorageBox::LocalStorageBox(
- QWidget*,
- not_null<Main::Session*> session,
- CreateTag)
- : _session(session)
- , _db(&session->data().cache())
- , _dbBig(&session->data().cacheBigFile()) {
- const auto &settings = session->local().cacheSettings();
- const auto &settingsBig = session->local().cacheBigFileSettings();
- _totalSizeLimit = settings.totalSizeLimit + settingsBig.totalSizeLimit;
- _mediaSizeLimit = settingsBig.totalSizeLimit;
- _timeLimit = settings.totalTimeLimit;
- }
- void LocalStorageBox::Show(not_null<Window::SessionController*> controller) {
- auto shared = std::make_shared<object_ptr<LocalStorageBox>>(
- Box<LocalStorageBox>(&controller->session(), CreateTag()));
- const auto weak = shared->data();
- rpl::combine(
- controller->session().data().cache().statsOnMain(),
- controller->session().data().cacheBigFile().statsOnMain()
- ) | rpl::start_with_next([=](
- Database::Stats &&stats,
- Database::Stats &&statsBig) {
- weak->update(std::move(stats), std::move(statsBig));
- if (auto &strong = *shared) {
- controller->uiShow()->show(std::move(strong));
- }
- }, weak->lifetime());
- }
- void LocalStorageBox::prepare() {
- setTitle(tr::lng_local_storage_title());
- addButton(tr::lng_box_ok(), [this] { closeBox(); });
- setupControls();
- }
- void LocalStorageBox::updateRow(
- not_null<Ui::SlideWrap<Row>*> row,
- const Database::TaggedSummary *data) {
- const auto summary = (_rows.find(0)->second == row);
- const auto shown = (data && data->count && data->totalSize) || summary;
- if (shown) {
- row->entity()->update(*data);
- }
- row->toggle(shown, anim::type::normal);
- }
- void LocalStorageBox::update(
- Database::Stats &&stats,
- Database::Stats &&statsBig) {
- _stats = std::move(stats);
- _statsBig = std::move(statsBig);
- if (const auto i = _rows.find(0); i != end(_rows)) {
- i->second->entity()->toggleProgress(
- _stats.clearing || _statsBig.clearing);
- }
- for (const auto &entry : _rows) {
- if (entry.first == kFakeMediaCacheTag) {
- updateRow(entry.second, &_statsBig.full);
- } else if (entry.first) {
- const auto i = _stats.tagged.find(entry.first);
- updateRow(
- entry.second,
- (i != end(_stats.tagged)) ? &i->second : nullptr);
- } else {
- const auto full = summary();
- updateRow(entry.second, &full);
- }
- }
- }
- auto LocalStorageBox::summary() const -> Database::TaggedSummary {
- auto result = _stats.full;
- result.count += _statsBig.full.count;
- result.totalSize += _statsBig.full.totalSize;
- return result;
- }
- void LocalStorageBox::clearByTag(uint16 tag) {
- if (tag == kFakeMediaCacheTag) {
- _dbBig->clear();
- } else if (tag) {
- _db->clearByTag(tag);
- } else {
- _db->clear();
- _dbBig->clear();
- Ui::Emoji::ClearIrrelevantCache();
- }
- }
- void LocalStorageBox::setupControls() {
- const auto container = setInnerWidget(
- object_ptr<Ui::VerticalLayout>(this),
- st::defaultMultiSelect.scroll);
- const auto createRow = [&](
- uint16 tag,
- Fn<QString(size_type)> title,
- rpl::producer<QString> clear,
- const Database::TaggedSummary &data) {
- auto result = container->add(object_ptr<Ui::SlideWrap<Row>>(
- container,
- object_ptr<Row>(
- container,
- std::move(title),
- std::move(clear),
- data)));
- const auto shown = (data.count && data.totalSize) || !tag;
- result->toggle(shown, anim::type::instant);
- result->entity()->clearRequests(
- ) | rpl::start_with_next([=] {
- clearByTag(tag);
- }, result->lifetime());
- _rows.emplace(tag, result);
- return result;
- };
- auto tracker = Ui::MultiSlideTracker();
- const auto createTagRow = [&](uint8 tag, auto &&titleFactory) {
- static const auto empty = Database::TaggedSummary();
- const auto i = _stats.tagged.find(tag);
- const auto &data = (i != end(_stats.tagged)) ? i->second : empty;
- auto factory = std::forward<decltype(titleFactory)>(titleFactory);
- auto title = [factory = std::move(factory)](size_type count) {
- return factory(tr::now, lt_count, count);
- };
- tracker.track(createRow(
- tag,
- std::move(title),
- tr::lng_local_storage_clear_some(),
- data));
- };
- auto summaryTitle = [](size_type) {
- return tr::lng_local_storage_summary(tr::now);
- };
- auto mediaCacheTitle = [](size_type) {
- return tr::lng_local_storage_media(tr::now);
- };
- createRow(
- 0,
- std::move(summaryTitle),
- tr::lng_local_storage_clear(),
- summary());
- setupLimits(container);
- const auto shadow = container->add(object_ptr<Ui::SlideWrap<>>(
- container,
- object_ptr<Ui::PlainShadow>(container),
- st::localStorageRowPadding));
- createTagRow(Data::kImageCacheTag, tr::lng_local_storage_image);
- createTagRow(Data::kStickerCacheTag, tr::lng_local_storage_sticker);
- createTagRow(Data::kVoiceMessageCacheTag, tr::lng_local_storage_voice);
- createTagRow(Data::kVideoMessageCacheTag, tr::lng_local_storage_round);
- createTagRow(Data::kAnimationCacheTag, tr::lng_local_storage_animation);
- tracker.track(createRow(
- kFakeMediaCacheTag,
- std::move(mediaCacheTitle),
- tr::lng_local_storage_clear_some(),
- _statsBig.full));
- shadow->toggleOn(
- std::move(tracker).atLeastOneShownValue()
- );
- container->resizeToWidth(st::boxWidth);
- container->heightValue(
- ) | rpl::start_with_next([=](int height) {
- setDimensions(st::boxWidth, height);
- }, container->lifetime());
- }
- template <
- typename Value,
- typename Convert,
- typename Callback,
- typename>
- not_null<Ui::MediaSlider*> LocalStorageBox::createLimitsSlider(
- not_null<Ui::VerticalLayout*> container,
- int valuesCount,
- Convert &&convert,
- Value currentValue,
- Callback &&callback) {
- const auto label = container->add(
- object_ptr<Ui::LabelSimple>(container, st::localStorageLimitLabel),
- st::localStorageLimitLabelMargin);
- callback(label, currentValue);
- const auto slider = container->add(
- object_ptr<Ui::MediaSlider>(container, st::localStorageLimitSlider),
- st::localStorageLimitMargin);
- slider->resize(st::localStorageLimitSlider.seekSize);
- slider->setPseudoDiscrete(
- valuesCount,
- std::forward<Convert>(convert),
- currentValue,
- [=, callback = std::forward<Callback>(callback)](Value value) {
- callback(label, value);
- });
- return slider;
- }
- void LocalStorageBox::updateMediaLimit() {
- const auto good = [&](int64 mediaLimit) {
- return (_totalSizeLimit - mediaLimit >= kMinimalSizeLimit);
- };
- if (good(_mediaSizeLimit) || !_mediaSlider || !_mediaLabel) {
- return;
- }
- auto index = 1;
- while ((index < kMediaSizeLimitsCount)
- && (MediaSizeLimit(index) * 2 <= _totalSizeLimit)) {
- ++index;
- }
- --index;
- _mediaSizeLimit = MediaSizeLimit(index);
- _mediaSlider->setValue(index / float64(kMediaSizeLimitsCount - 1));
- updateMediaLabel();
- Ensures(good(_mediaSizeLimit));
- }
- void LocalStorageBox::updateTotalLimit() {
- const auto good = [&](int64 totalLimit) {
- return (totalLimit - _mediaSizeLimit >= kMinimalSizeLimit);
- };
- if (good(_totalSizeLimit) || !_totalSlider || !_totalLabel) {
- return;
- }
- auto index = kTotalSizeLimitsCount - 1;
- while ((index > 0)
- && (TotalSizeLimit(index - 1) >= 2 * _mediaSizeLimit)) {
- --index;
- }
- _totalSizeLimit = TotalSizeLimit(index);
- _totalSlider->setValue(index / float64(kTotalSizeLimitsCount - 1));
- updateTotalLabel();
- Ensures(good(_totalSizeLimit));
- }
- void LocalStorageBox::updateTotalLabel() {
- Expects(_totalLabel != nullptr);
- const auto text = SizeLimitText(_totalSizeLimit);
- _totalLabel->setText(tr::lng_local_storage_size_limit(tr::now, lt_size, text));
- }
- void LocalStorageBox::updateMediaLabel() {
- Expects(_mediaLabel != nullptr);
- const auto text = SizeLimitText(_mediaSizeLimit);
- _mediaLabel->setText(tr::lng_local_storage_media_limit(tr::now, lt_size, text));
- }
- void LocalStorageBox::setupLimits(not_null<Ui::VerticalLayout*> container) {
- container->add(
- object_ptr<Ui::PlainShadow>(container),
- st::localStorageRowPadding);
- _totalSlider = createLimitsSlider(
- container,
- kTotalSizeLimitsCount,
- TotalSizeLimit,
- _totalSizeLimit,
- [=](not_null<Ui::LabelSimple*> label, int64 limit) {
- _totalSizeLimit = limit;
- _totalLabel = label;
- updateTotalLabel();
- updateMediaLimit();
- limitsChanged();
- });
- _mediaSlider = createLimitsSlider(
- container,
- kMediaSizeLimitsCount,
- MediaSizeLimit,
- _mediaSizeLimit,
- [=](not_null<Ui::LabelSimple*> label, int64 limit) {
- _mediaSizeLimit = limit;
- _mediaLabel = label;
- updateMediaLabel();
- updateTotalLimit();
- limitsChanged();
- });
- createLimitsSlider(
- container,
- kTimeLimitsCount,
- TimeLimit,
- LimitToValue(_timeLimit),
- [=](not_null<Ui::LabelSimple*> label, size_type limit) {
- _timeLimit = ValueToLimit(limit);
- const auto text = TimeLimitText(_timeLimit);
- label->setText(tr::lng_local_storage_time_limit(tr::now, lt_limit, text));
- limitsChanged();
- });
- }
- void LocalStorageBox::limitsChanged() {
- const auto &settings = _session->local().cacheSettings();
- const auto &settingsBig = _session->local().cacheBigFileSettings();
- const auto sizeLimit = _totalSizeLimit - _mediaSizeLimit;
- const auto changed = (settings.totalSizeLimit != sizeLimit)
- || (settingsBig.totalSizeLimit != _mediaSizeLimit)
- || (settings.totalTimeLimit != _timeLimit)
- || (settingsBig.totalTimeLimit != _timeLimit);
- if (_limitsChanged != changed) {
- _limitsChanged = changed;
- clearButtons();
- if (_limitsChanged) {
- addButton(tr::lng_settings_save(), [=] { save(); });
- addButton(tr::lng_cancel(), [=] { closeBox(); });
- } else {
- addButton(tr::lng_box_ok(), [=] { closeBox(); });
- }
- }
- }
- void LocalStorageBox::save() {
- if (!_limitsChanged) {
- closeBox();
- return;
- }
- auto update = Storage::Cache::Database::SettingsUpdate();
- update.totalSizeLimit = _totalSizeLimit - _mediaSizeLimit;
- update.totalTimeLimit = _timeLimit;
- auto updateBig = Storage::Cache::Database::SettingsUpdate();
- updateBig.totalSizeLimit = _mediaSizeLimit;
- updateBig.totalTimeLimit = _timeLimit;
- _session->local().updateCacheSettings(update, updateBig);
- _session->data().cache().updateSettings(update);
- closeBox();
- }
|