Allow changing limits for cache in Settings.

This commit is contained in:
John Preston 2018-08-31 16:01:42 +03:00
parent 5733f4079f
commit 069232ec1b
11 changed files with 334 additions and 62 deletions

View File

@ -366,6 +366,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_local_storage_round#other" = "{count} video messages";
"lng_local_storage_animation#one" = "{count} animation";
"lng_local_storage_animation#other" = "{count} animations";
"lng_local_storage_size_limit" = "Total size limit: {size}";
"lng_local_storage_time_limit" = "Clear files older than: {limit}";
"lng_local_storage_limit_weeks#one" = "{count} week";
"lng_local_storage_limit_weeks#other" = "{count} weeks";
"lng_local_storage_limit_months#one" = "{count} month";
"lng_local_storage_limit_months#other" = "{count} months";
"lng_local_storage_limit_never" = "Never";
"lng_local_storage_summary" = "Summary";
"lng_local_storage_clear_some" = "Clear";
"lng_local_storage_clear" = "Clear all";

View File

@ -355,7 +355,7 @@ peerListBox: PeerList(defaultPeerList) {
}
localStorageRowHeight: 50px;
localStorageRowPadding: margins(23px, 5px, 23px, 5px);
localStorageRowPadding: margins(23px, 5px, 20px, 5px);
localStorageRowTitle: FlatLabel(defaultFlatLabel) {
textFg: windowBoldFg;
maxHeight: 20px;
@ -375,6 +375,14 @@ localStorageRowSize: FlatLabel(defaultFlatLabel) {
}
}
localStorageClear: defaultBoxButton;
localStorageLimitLabel: LabelSimple(defaultLabelSimple) {
font: boxTextFont;
}
localStorageLimitLabelMargin: margins(23px, 10px, 20px, 5px);
localStorageLimitSlider: MediaSlider(defaultContinuousSlider) {
seekSize: size(15px, 15px);
}
localStorageLimitMargin: margins(23px, 5px, 20px, 10px);
shareRowsTop: 12px;
shareRowHeight: 108px;

View File

@ -12,14 +12,97 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/effects/radial_animation.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/effects/radial_animation.h"
#include "storage/localstorage.h"
#include "storage/cache/storage_cache_database.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "auth_session.h"
#include "layout.h"
namespace {
constexpr auto kSizeLimitsCount = 20;
constexpr auto kTimeLimitsCount = 16;
constexpr auto kMaxTimeLimitValue = std::numeric_limits<size_type>::max();
int64 SizeLimitInMB(int index) {
if (index < 10) {
return int64(index + 1) * 100;
}
return int64(index - 9) * 1024;
}
int64 SizeLimit(int index) {
return SizeLimitInMB(index) * 1024 * 1024;
}
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)
? lng_local_storage_limit_months(lt_count, months)
: (limit > 0)
? lng_local_storage_limit_weeks(lt_count, weeks)
: lang(lng_local_storage_limit_never);
}
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(
@ -134,7 +217,7 @@ int LocalStorageBox::Row::resizeGetHeight(int newWidth) {
newWidth);
}
_clear->moveToRight(
st::boxButtonPadding.right(),
st::boxLayerButtonPadding.right(),
(height - _clear->height()) / 2,
newWidth);
return height;
@ -174,6 +257,9 @@ LocalStorageBox::LocalStorageBox(
not_null<Database*> db,
CreateTag)
: _db(db) {
const auto &settings = Local::cacheSettings();
_sizeLimit = settings.totalSizeLimit;
_timeLimit = settings.totalTimeLimit;
}
void LocalStorageBox::Show(not_null<Database*> db) {
@ -234,17 +320,17 @@ void LocalStorageBox::clearByTag(uint8 tag) {
}
void LocalStorageBox::setupControls() {
_content.create(this);
const auto container = setInnerWidget(
object_ptr<Ui::VerticalLayout>(this));
const auto createRow = [&](
uint8 tag,
Fn<QString(size_type)> title,
Fn<QString()> clear,
const Database::TaggedSummary &data) {
auto result = _content->add(object_ptr<Ui::SlideWrap<Row>>(
_content,
auto result = container->add(object_ptr<Ui::SlideWrap<Row>>(
container,
object_ptr<Row>(
_content,
container,
std::move(title),
std::move(clear),
data)));
@ -280,11 +366,11 @@ void LocalStorageBox::setupControls() {
std::move(summaryTitle),
langFactory(lng_local_storage_clear),
_stats.full);
const auto shadow = _content->add(object_ptr<Ui::SlideWrap<>>(
_content,
object_ptr<Ui::PlainShadow>(_content),
st::localStorageRowPadding)
);
setupLimits(container);
const auto shadow = container->add(object_ptr<Ui::SlideWrap<>>(
container,
object_ptr<Ui::PlainShadow>(container),
st::localStorageRowPadding));
createTagRow(Data::kImageCacheTag, lng_local_storage_image);
createTagRow(Data::kStickerCacheTag, lng_local_storage_sticker);
createTagRow(Data::kVoiceMessageCacheTag, lng_local_storage_voice);
@ -293,11 +379,98 @@ void LocalStorageBox::setupControls() {
shadow->toggleOn(
std::move(tracker).atLeastOneShownValue()
);
_content->resizeToWidth(st::boxWidth);
_content->heightValue(
container->resizeToWidth(st::boxWidth);
container->heightValue(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWidth, height);
}, _content->lifetime());
}, container->lifetime());
}
template <
typename Value,
typename Convert,
typename Callback,
typename>
void 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);
});
}
void LocalStorageBox::setupLimits(not_null<Ui::VerticalLayout*> container) {
const auto shadow = container->add(
object_ptr<Ui::PlainShadow>(container),
st::localStorageRowPadding);
createLimitsSlider(
container,
kSizeLimitsCount,
SizeLimit,
_sizeLimit,
[=](not_null<Ui::LabelSimple*> label, int64 limit) {
const auto text = SizeLimitText(limit);
label->setText(lng_local_storage_size_limit(lt_size, text));
_sizeLimit = limit;
limitsChanged();
});
createLimitsSlider(
container,
kTimeLimitsCount,
TimeLimit,
LimitToValue(_timeLimit),
[=](not_null<Ui::LabelSimple*> label, size_type limit) {
const auto text = TimeLimitText(ValueToLimit(limit));
label->setText(lng_local_storage_time_limit(lt_limit, text));
_timeLimit = limit;
limitsChanged();
});
}
void LocalStorageBox::limitsChanged() {
const auto &settings = Local::cacheSettings();
const auto changed = (settings.totalSizeLimit != _sizeLimit)
|| (settings.totalTimeLimit != _timeLimit);
if (_limitsChanged != changed) {
_limitsChanged = changed;
clearButtons();
if (_limitsChanged) {
addButton(langFactory(lng_settings_save), [=] { save(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [=] { closeBox(); });
}
}
}
void LocalStorageBox::save() {
if (!_limitsChanged) {
closeBox();
return;
}
auto update = Storage::Cache::Database::SettingsUpdate();
update.totalSizeLimit = _sizeLimit;
update.totalTimeLimit = _timeLimit;
Local::updateCacheSettings(update);
Auth().data().cache().updateSettings(update);
closeBox();
}
void LocalStorageBox::paintEvent(QPaintEvent *e) {

View File

@ -20,6 +20,7 @@ namespace Ui {
class VerticalLayout;
template <typename Widget>
class SlideWrap;
class LabelSimple;
} // namespace Ui
class LocalStorageBox : public BoxContent {
@ -47,11 +48,34 @@ private:
not_null<Ui::SlideWrap<Row>*> row,
Database::TaggedSummary *data);
void setupControls();
void setupLimits(not_null<Ui::VerticalLayout*> container);
void limitsChanged();
void save();
template <
typename Value,
typename Convert,
typename Callback,
typename = std::enable_if_t<
rpl::details::is_callable_plain_v<
Callback,
not_null<Ui::LabelSimple*>,
Value>
&& std::is_same_v<Value, decltype(std::declval<Convert>()(1))>>>
void createLimitsSlider(
not_null<Ui::VerticalLayout*> container,
int valuesCount,
Convert &&convert,
Value currentValue,
Callback &&callback);
not_null<Storage::Cache::Database*> _db;
Database::Stats _stats;
object_ptr<Ui::VerticalLayout> _content = { nullptr };
base::flat_map<uint8, not_null<Ui::SlideWrap<Row>*>> _rows;
int64 _sizeLimit = 0;
size_type _timeLimit = 0;
bool _limitsChanged = false;
};

View File

@ -21,16 +21,8 @@ exportHeaderLabel: FlatLabel(boxTitle) {
}
}
exportHeaderPadding: margins(22px, 20px, 22px, 9px);
exportFileSizeSlider: MediaSlider {
width: 3px;
activeFg: mediaPlayerActiveFg;
inactiveFg: mediaPlayerInactiveFg;
activeFgOver: mediaPlayerActiveFg;
inactiveFgOver: mediaPlayerInactiveFg;
activeFgDisabled: mediaPlayerInactiveFg;
inactiveFgDisabled: windowBg;
exportFileSizeSlider: MediaSlider(defaultContinuousSlider) {
seekSize: size(15px, 15px);
duration: 150;
}
exportFileSizeLabel: LabelSimple(defaultLabelSimple) {
font: boxTextFont;

View File

@ -469,27 +469,19 @@ void SettingsWidget::addSizeSlider(
object_ptr<Ui::MediaSlider>(container, st::exportFileSizeSlider),
st::exportFileSizePadding);
slider->resize(st::exportFileSizeSlider.seekSize);
slider->setAlwaysDisplayMarker(true);
slider->setDirection(Ui::ContinuousSlider::Direction::Horizontal);
for (auto i = 0; i != kSizeValueCount + 1; ++i) {
if (readData().media.sizeLimit <= SizeLimitByIndex(i)) {
slider->setValue(i / float64(kSizeValueCount));
break;
}
}
slider->setPseudoDiscrete(
kSizeValueCount + 1,
SizeLimitByIndex,
readData().media.sizeLimit,
[=](int limit) {
changeData([&](Settings &data) {
data.media.sizeLimit = limit;
});
});
const auto label = Ui::CreateChild<Ui::LabelSimple>(
container.get(),
st::exportFileSizeLabel);
slider->setAdjustCallback([=](float64 value) {
return std::round(value * kSizeValueCount) / kSizeValueCount;
});
slider->setChangeProgressCallback([=](float64 value) {
const auto index = int(std::round(value * kSizeValueCount));
changeData([&](Settings &data) {
data.media.sizeLimit = SizeLimitByIndex(index);
});
});
value() | rpl::map([](const Settings &data) {
return data.media.sizeLimit;
}) | rpl::start_with_next([=](int sizeLimit) {
@ -511,7 +503,6 @@ void SettingsWidget::addSizeSlider(
st::exportFileSizePadding.right(),
geometry.y() - label->height() - st::exportFileSizeLabelBottom);
}, label->lifetime());
}
void SettingsWidget::refreshButtons(

View File

@ -193,17 +193,7 @@ mediaPlayerPanelPlaySkip: 7px;
mediaPlayerPanelPlayTop: 58px;
mediaPlayerPanelPlaybackTop: 32px;
mediaPlayerPanelPlaybackPadding: 8px;
mediaPlayerPanelPlayback: MediaSlider {
width: 3px;
activeFg: mediaPlayerActiveFg;
inactiveFg: mediaPlayerInactiveFg;
activeFgOver: mediaPlayerActiveFg;
inactiveFgOver: mediaPlayerInactiveFg;
activeFgDisabled: mediaPlayerInactiveFg;
inactiveFgDisabled: windowBg;
seekSize: size(9px, 9px);
duration: 150;
}
mediaPlayerPanelPlayback: defaultContinuousSlider;
mediaPlayerPanelVolumeTop: 65px;
mediaPlayerPanelVolumeSkip: 3px;

View File

@ -50,6 +50,7 @@ constexpr auto kSinglePeerTypeChannel = qint32(3);
constexpr auto kSinglePeerTypeSelf = qint32(4);
constexpr auto kSinglePeerTypeEmpty = qint32(0);
using Database = Storage::Cache::Database;
using FileKey = quint64;
constexpr char tdfMagic[] = { 'T', 'D', 'F', '$' };
@ -590,6 +591,7 @@ enum {
dbiTxtDomainString = 0x53,
dbiThemeKey = 0x54,
dbiTileBackground = 0x55,
dbiCacheSettings = 0x56,
dbiEncryptedWithSalt = 333,
dbiEncrypted = 444,
@ -647,6 +649,8 @@ bool _readingUserSettings = false;
FileKey _userSettingsKey = 0;
FileKey _recentHashtagsAndBotsKey = 0;
bool _recentHashtagsAndBotsWereRead = false;
qint64 _cacheTotalSizeLimit = Database::Settings().totalSizeLimit;
qint32 _cacheTotalTimeLimit = Database::Settings().totalTimeLimit;
FileKey _exportSettingsKey = 0;
@ -1010,6 +1014,20 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
cSetUseExternalVideoPlayer(v == 1);
} break;
case dbiCacheSettings: {
qint64 size;
qint32 time;
stream >> size >> time;
if (!_checkStreamStatus(stream)
|| size <= Database::Settings().maxDataSize
|| time < 0) {
return false;
}
_cacheTotalSizeLimit = size;
_cacheTotalTimeLimit = time;
}
case dbiSoundNotify: {
qint32 v;
stream >> v;
@ -1905,6 +1923,7 @@ void _writeUserSettings() {
size += sizeof(quint32) + 3 * sizeof(qint32);
size += sizeof(quint32) + 2 * sizeof(qint32);
size += sizeof(quint32) + 2 * sizeof(qint32);
size += sizeof(quint32) + sizeof(qint64) + sizeof(qint32);
if (!Global::HiddenPinnedMessages().isEmpty()) {
size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId));
}
@ -1940,6 +1959,7 @@ void _writeUserSettings() {
data.stream << quint32(dbiModerateMode) << qint32(Global::ModerateModeEnabled() ? 1 : 0);
data.stream << quint32(dbiAutoPlay) << qint32(cAutoPlayGif() ? 1 : 0);
data.stream << quint32(dbiUseExternalVideoPlayer) << qint32(cUseExternalVideoPlayer());
data.stream << quint32(dbiCacheSettings) << qint64(_cacheTotalSizeLimit) << qint32(_cacheTotalTimeLimit);
if (!userData.isEmpty()) {
data.stream << quint32(dbiAuthSessionSettings) << userData;
}
@ -2608,6 +2628,8 @@ void reset() {
Window::Theme::Background()->reset();
_userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = _exportSettingsKey = 0;
_oldMapVersion = _oldSettingsVersion = 0;
_cacheTotalSizeLimit = Database::Settings().totalSizeLimit;
_cacheTotalTimeLimit = Database::Settings().totalTimeLimit;
StoredAuthSessionCache.reset();
_mapChanged = true;
_writeMap(WriteMapWhen::Now);
@ -2987,18 +3009,33 @@ QString cachePath() {
return _userDbPath + "cache";
}
Storage::Cache::Database::Settings cacheSettings() {
auto result = Storage::Cache::Database::Settings();
result.clearOnWrongKey = true;
return result;
}
Storage::EncryptionKey cacheKey() {
Expects(LocalKey != nullptr);
return Storage::EncryptionKey(bytes::make_vector(LocalKey->data()));
}
Storage::Cache::Database::Settings cacheSettings() {
auto result = Storage::Cache::Database::Settings();
result.clearOnWrongKey = true;
result.totalSizeLimit = _cacheTotalSizeLimit;
result.totalTimeLimit = _cacheTotalTimeLimit;
return result;
}
void updateCacheSettings(Storage::Cache::Database::SettingsUpdate &update) {
Expects(update.totalSizeLimit > Database::Settings().maxDataSize);
Expects(update.totalTimeLimit >= 0);
if (_cacheTotalSizeLimit == update.totalSizeLimit
&& _cacheTotalTimeLimit == update.totalTimeLimit) {
return;
}
_cacheTotalSizeLimit = update.totalSizeLimit;
_cacheTotalTimeLimit = update.totalTimeLimit;
_writeUserSettings();
}
class CountWaveformTask : public Task {
public:
CountWaveformTask(DocumentData *doc)

View File

@ -105,8 +105,9 @@ void writeFileLocation(MediaKey location, const FileLocation &local);
FileLocation readFileLocation(MediaKey location, bool check = true);
QString cachePath();
Storage::Cache::Database::Settings cacheSettings();
Storage::EncryptionKey cacheKey();
Storage::Cache::Database::Settings cacheSettings();
void updateCacheSettings(Storage::Cache::Database::SettingsUpdate &update);
void countVoiceWaveform(DocumentData *document);

View File

@ -130,6 +130,43 @@ public:
}
void disablePaint(bool disabled);
template <
typename Value,
typename Convert,
typename Callback,
typename = std::enable_if_t<
rpl::details::is_callable_plain_v<Callback, Value>
&& std::is_same_v<Value, decltype(std::declval<Convert>()(1))>>>
void setPseudoDiscrete(
int valuesCount,
Convert &&convert,
Value current,
Callback &&callback) {
Expects(valuesCount > 1);
setAlwaysDisplayMarker(true);
setDirection(Ui::ContinuousSlider::Direction::Horizontal);
const auto sectionsCount = (valuesCount - 1);
for (auto index = index_type(); index != valuesCount; ++index) {
if (current <= convert(index)) {
setValue(index / float64(sectionsCount));
break;
}
}
setAdjustCallback([=](float64 value) {
return std::round(value * sectionsCount) / sectionsCount;
});
setChangeProgressCallback([
=,
convert = std::forward<Convert>(convert),
callback = std::forward<Callback>(callback)
](float64 value) {
const auto index = int(std::round(value * sectionsCount));
callback(convert(index));
});
}
protected:
void paintEvent(QPaintEvent *e) override;

View File

@ -888,6 +888,18 @@ defaultPanelAnimation: PanelAnimation {
shadow: defaultRoundShadow;
}
defaultContinuousSlider: MediaSlider {
width: 3px;
activeFg: mediaPlayerActiveFg;
inactiveFg: mediaPlayerInactiveFg;
activeFgOver: mediaPlayerActiveFg;
inactiveFgOver: mediaPlayerInactiveFg;
activeFgDisabled: mediaPlayerInactiveFg;
inactiveFgDisabled: windowBg;
seekSize: size(9px, 9px);
duration: 150;
}
defaultRoundCheckbox: RoundCheckbox {
border: windowBg;
bgActive: windowBgActive;