mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-06 20:03:12 +00:00
467 lines
13 KiB
C++
467 lines
13 KiB
C++
/*
|
|
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/auto_download_box.h"
|
|
|
|
#include "boxes/peer_list_box.h"
|
|
#include "boxes/peer_list_controllers.h"
|
|
#include "history/history.h"
|
|
#include "lang/lang_keys.h"
|
|
#include "main/main_session.h"
|
|
#include "main/main_session_settings.h"
|
|
#include "data/data_peer.h"
|
|
#include "data/data_session.h"
|
|
#include "data/data_auto_download.h"
|
|
#include "ui/vertical_list.h"
|
|
#include "ui/widgets/continuous_sliders.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/wrap/vertical_layout.h"
|
|
#include "ui/wrap/wrap.h"
|
|
#include "storage/localstorage.h"
|
|
#include "settings/settings_common.h"
|
|
#include "export/view/export_view_settings.h"
|
|
#include "styles/style_layers.h"
|
|
#include "styles/style_boxes.h"
|
|
#include "styles/style_settings.h"
|
|
|
|
namespace {
|
|
|
|
constexpr auto kMegabyte = 1024 * 1024;
|
|
constexpr auto kDefaultDownloadLimit = 10 * kMegabyte;
|
|
constexpr auto kDefaultAutoPlayLimit = 50 * kMegabyte;
|
|
|
|
using Type = Data::AutoDownload::Type;
|
|
using Source = Data::AutoDownload::Source;
|
|
using Override = Data::AutoDownload::Override;
|
|
|
|
[[nodiscard]] bool PeerMatchesSource(
|
|
not_null<PeerData*> peer,
|
|
Source source) {
|
|
if (peer->isSelf() || peer->isServiceUser() || peer->isRepliesChat()) {
|
|
return false;
|
|
}
|
|
switch (source) {
|
|
case Source::User: return peer->isUser();
|
|
case Source::Group: return peer->isChat() || peer->isMegagroup();
|
|
case Source::Channel:
|
|
return peer->isChannel() && !peer->isMegagroup();
|
|
}
|
|
Unexpected("Source in PeerMatchesSource.");
|
|
}
|
|
|
|
struct PeerBuckets {
|
|
base::flat_set<PeerId> always;
|
|
base::flat_set<PeerId> never;
|
|
};
|
|
|
|
[[nodiscard]] PeerBuckets BucketsForSource(
|
|
const Data::AutoDownload::Full &settings,
|
|
not_null<Data::Session*> owner,
|
|
Source source) {
|
|
auto result = PeerBuckets();
|
|
settings.enumeratePeerOverrides([&](PeerId peerId, Override value) {
|
|
const auto peer = owner->peer(peerId);
|
|
if (!PeerMatchesSource(peer, source)) {
|
|
return;
|
|
} else if (value == Override::ForceAllow) {
|
|
result.always.emplace(peerId);
|
|
} else if (value == Override::ForceDeny) {
|
|
result.never.emplace(peerId);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
class ExceptionsBoxController final : public ChatsListBoxController {
|
|
public:
|
|
ExceptionsBoxController(
|
|
not_null<Main::Session*> session,
|
|
Source source,
|
|
base::flat_set<PeerId> selected);
|
|
|
|
Main::Session &session() const override;
|
|
void rowClicked(not_null<PeerListRow*> row) override;
|
|
|
|
protected:
|
|
void prepareViewHook() override;
|
|
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
|
|
|
private:
|
|
const not_null<Main::Session*> _session;
|
|
const Source _source;
|
|
base::flat_set<PeerId> _selected;
|
|
|
|
};
|
|
|
|
ExceptionsBoxController::ExceptionsBoxController(
|
|
not_null<Main::Session*> session,
|
|
Source source,
|
|
base::flat_set<PeerId> selected)
|
|
: ChatsListBoxController(session)
|
|
, _session(session)
|
|
, _source(source)
|
|
, _selected(std::move(selected)) {
|
|
}
|
|
|
|
Main::Session &ExceptionsBoxController::session() const {
|
|
return *_session;
|
|
}
|
|
|
|
void ExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
|
delegate()->peerListSetRowChecked(row, !row->checked());
|
|
}
|
|
|
|
void ExceptionsBoxController::prepareViewHook() {
|
|
auto &owner = _session->data();
|
|
auto peers = std::vector<not_null<PeerData*>>();
|
|
peers.reserve(_selected.size());
|
|
for (const auto &peerId : _selected) {
|
|
peers.push_back(owner.peer(peerId));
|
|
}
|
|
delegate()->peerListAddSelectedPeers(peers);
|
|
}
|
|
|
|
auto ExceptionsBoxController::createRow(not_null<History*> history)
|
|
-> std::unique_ptr<Row> {
|
|
const auto peer = history->peer;
|
|
return PeerMatchesSource(peer, _source)
|
|
? std::make_unique<Row>(history)
|
|
: nullptr;
|
|
}
|
|
|
|
not_null<int64*> AddSizeLimitSlider(
|
|
not_null<Ui::VerticalLayout*> container,
|
|
const base::flat_map<Type, int64> &values,
|
|
int64 defaultValue) {
|
|
using Pair = base::flat_map<Type, int64>::value_type;
|
|
|
|
const auto limits = Ui::CreateChild<rpl::event_stream<int64>>(
|
|
container.get());
|
|
const auto currentLimit = ranges::max_element(
|
|
values,
|
|
std::less<>(),
|
|
[](Pair pair) { return pair.second; })->second;
|
|
const auto startLimit = currentLimit ? currentLimit : defaultValue;
|
|
const auto result = Ui::CreateChild<int64>(container.get(), startLimit);
|
|
Settings::AddButtonWithLabel(
|
|
container,
|
|
tr::lng_media_size_limit(),
|
|
limits->events_starting_with_copy(
|
|
startLimit
|
|
) | rpl::map([](int64 value) {
|
|
return tr::lng_media_size_up_to(
|
|
tr::now,
|
|
lt_size,
|
|
QString::number(value / kMegabyte) + " MB");
|
|
}),
|
|
st::autoDownloadLimitButton
|
|
)->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
const auto slider = container->add(
|
|
object_ptr<Ui::MediaSlider>(container, st::autoDownloadLimitSlider),
|
|
st::autoDownloadLimitPadding);
|
|
slider->resize(st::autoDownloadLimitSlider.seekSize);
|
|
slider->setPseudoDiscrete(
|
|
Export::View::kSizeValueCount,
|
|
Export::View::SizeLimitByIndex,
|
|
*result,
|
|
[=](int64 value) {
|
|
*result = value;
|
|
limits->fire_copy(value);
|
|
});
|
|
return result;
|
|
}
|
|
} // namespace
|
|
|
|
AutoDownloadBox::AutoDownloadBox(
|
|
QWidget*,
|
|
not_null<Main::Session*> session,
|
|
Data::AutoDownload::Source source)
|
|
: _session(session)
|
|
, _source(source) {
|
|
}
|
|
|
|
void AutoDownloadBox::prepare() {
|
|
setupContent();
|
|
}
|
|
|
|
void AutoDownloadBox::setupContent() {
|
|
using namespace rpl::mappers;
|
|
using namespace Settings;
|
|
using namespace Data::AutoDownload;
|
|
using Type = Data::AutoDownload::Type;
|
|
using Pair = base::flat_map<Type, int64>::value_type;
|
|
|
|
setTitle(tr::lng_profile_settings_section());
|
|
|
|
const auto settings = &_session->settings().autoDownload();
|
|
|
|
auto wrap = object_ptr<Ui::VerticalLayout>(this);
|
|
const auto content = wrap.data();
|
|
setInnerWidget(object_ptr<Ui::OverrideMargins>(
|
|
this,
|
|
std::move(wrap)));
|
|
|
|
const auto add = [&](
|
|
not_null<base::flat_map<Type, int64>*> values,
|
|
Type type,
|
|
rpl::producer<QString> label) {
|
|
const auto value = settings->bytesLimit(_source, type);
|
|
content->add(object_ptr<Ui::SettingsButton>(
|
|
content,
|
|
std::move(label),
|
|
st::settingsButtonNoIcon
|
|
))->toggleOn(
|
|
rpl::single(value > 0)
|
|
)->toggledChanges(
|
|
) | rpl::on_next([=](bool enabled) {
|
|
(*values)[type] = enabled ? 1 : 0;
|
|
}, content->lifetime());
|
|
values->emplace(type, value);
|
|
};
|
|
|
|
AddSubsectionTitle(content, tr::lng_media_auto_title());
|
|
|
|
const auto downloadValues = Ui::CreateChild<base::flat_map<Type, int64>>(
|
|
content);
|
|
add(downloadValues, Type::Photo, tr::lng_media_photo_title());
|
|
add(downloadValues, Type::File, tr::lng_media_file_title());
|
|
|
|
const auto downloadLimit = AddSizeLimitSlider(
|
|
content,
|
|
*downloadValues,
|
|
kDefaultDownloadLimit);
|
|
|
|
AddSkip(content);
|
|
AddSubsectionTitle(content, tr::lng_media_auto_play());
|
|
|
|
const auto autoPlayValues = Ui::CreateChild<base::flat_map<Type, int64>>(
|
|
content);
|
|
add(
|
|
autoPlayValues,
|
|
Type::AutoPlayVideoMessage,
|
|
tr::lng_media_video_messages_title());
|
|
add(autoPlayValues, Type::AutoPlayVideo, tr::lng_media_video_title());
|
|
add(autoPlayValues, Type::AutoPlayGIF, tr::lng_media_animation_title());
|
|
|
|
const auto autoPlayLimit = AddSizeLimitSlider(
|
|
content,
|
|
*autoPlayValues,
|
|
kDefaultAutoPlayLimit);
|
|
|
|
const auto limitByType = [=](Type type) {
|
|
return (ranges::find(kAutoPlayTypes, type) != end(kAutoPlayTypes))
|
|
? *autoPlayLimit
|
|
: *downloadLimit;
|
|
};
|
|
|
|
const auto staged = content->lifetime().make_state<PeerBuckets>(
|
|
BucketsForSource(*settings, &_session->data(), _source));
|
|
|
|
AddSkip(content);
|
|
AddDivider(content);
|
|
AddSkip(content);
|
|
AddSubsectionTitle(content, tr::lng_media_auto_exceptions());
|
|
|
|
const auto countText = [source = _source](int count) {
|
|
switch (source) {
|
|
case Source::User:
|
|
return tr::lng_media_auto_exceptions_users(
|
|
tr::now,
|
|
lt_count,
|
|
count);
|
|
case Source::Group:
|
|
return tr::lng_media_auto_exceptions_groups(
|
|
tr::now,
|
|
lt_count,
|
|
count);
|
|
case Source::Channel:
|
|
return tr::lng_media_auto_exceptions_channels(
|
|
tr::now,
|
|
lt_count,
|
|
count);
|
|
}
|
|
Unexpected("Source in countText.");
|
|
};
|
|
const auto addText = [source = _source] {
|
|
switch (source) {
|
|
case Source::User:
|
|
return tr::lng_media_auto_exceptions_add_users(tr::now);
|
|
case Source::Group:
|
|
return tr::lng_media_auto_exceptions_add_groups(tr::now);
|
|
case Source::Channel:
|
|
return tr::lng_media_auto_exceptions_add_channels(tr::now);
|
|
}
|
|
Unexpected("Source in addText.");
|
|
};
|
|
|
|
const auto updates = content->lifetime().make_state<rpl::event_stream<>>();
|
|
const auto addExceptionButton = [&](
|
|
Override kind,
|
|
rpl::producer<QString> text,
|
|
rpl::producer<QString> title) {
|
|
auto label = updates->events_starting_with(
|
|
{}
|
|
) | rpl::map([=] {
|
|
const auto count = int((kind == Override::ForceAllow)
|
|
? staged->always.size()
|
|
: staged->never.size());
|
|
return count ? countText(count) : addText();
|
|
});
|
|
const auto button = AddButtonWithLabel(
|
|
content,
|
|
std::move(text),
|
|
std::move(label),
|
|
st::settingsButtonNoIcon);
|
|
button->setClickedCallback([=, title = std::move(title)]() mutable {
|
|
const auto &selected = (kind == Override::ForceAllow)
|
|
? staged->always
|
|
: staged->never;
|
|
auto controller = std::make_unique<ExceptionsBoxController>(
|
|
_session,
|
|
_source,
|
|
selected);
|
|
auto initBox = [=, this, title = std::move(title)](
|
|
not_null<PeerListBox*> box) mutable {
|
|
box->setTitle(std::move(title));
|
|
box->addButton(tr::lng_settings_save(), crl::guard(this, [=] {
|
|
auto &dest = (kind == Override::ForceAllow)
|
|
? staged->always
|
|
: staged->never;
|
|
auto &other = (kind == Override::ForceAllow)
|
|
? staged->never
|
|
: staged->always;
|
|
dest.clear();
|
|
for (const auto &peer : box->collectSelectedRows()) {
|
|
dest.emplace(peer->id);
|
|
other.remove(peer->id);
|
|
}
|
|
updates->fire({});
|
|
box->closeBox();
|
|
}));
|
|
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
|
};
|
|
getDelegate()->show(Box<PeerListBox>(
|
|
std::move(controller),
|
|
std::move(initBox)));
|
|
});
|
|
};
|
|
addExceptionButton(
|
|
Override::ForceAllow,
|
|
tr::lng_media_auto_always(),
|
|
tr::lng_media_auto_always_title());
|
|
addExceptionButton(
|
|
Override::ForceDeny,
|
|
tr::lng_media_auto_never(),
|
|
tr::lng_media_auto_never_title());
|
|
|
|
addButton(tr::lng_connection_save(), [=] {
|
|
auto &&values = ranges::views::concat(
|
|
*downloadValues,
|
|
*autoPlayValues);
|
|
auto allowMore = values | ranges::views::filter([&](Pair pair) {
|
|
const auto &[type, enabled] = pair;
|
|
const auto value = enabled ? limitByType(type) : 0;
|
|
const auto old = settings->bytesLimit(_source, type);
|
|
return (old < value);
|
|
}) | ranges::views::transform([](Pair pair) {
|
|
return pair.first;
|
|
});
|
|
const auto less = ranges::any_of(*autoPlayValues, [&](Pair pair) {
|
|
const auto &[type, enabled] = pair;
|
|
const auto value = enabled ? limitByType(type) : 0;
|
|
return value < settings->bytesLimit(_source, type);
|
|
});
|
|
const auto allowMoreTypes = base::flat_set<Type>(
|
|
allowMore.begin(),
|
|
allowMore.end());
|
|
|
|
const auto changed = ranges::any_of(values, [&](Pair pair) {
|
|
const auto &[type, enabled] = pair;
|
|
const auto value = enabled ? limitByType(type) : 0;
|
|
return value != settings->bytesLimit(_source, type);
|
|
});
|
|
|
|
const auto &kHidden = kStreamedTypes;
|
|
const auto hiddenChanged = ranges::any_of(kHidden, [&](Type type) {
|
|
const auto now = settings->bytesLimit(_source, type);
|
|
return (now > 0) && (now != limitByType(type));
|
|
});
|
|
|
|
const auto current = BucketsForSource(
|
|
*settings,
|
|
&_session->data(),
|
|
_source);
|
|
const auto exceptionsChanged = (current.always != staged->always)
|
|
|| (current.never != staged->never);
|
|
const auto exceptionsAllowMore = ranges::any_of(
|
|
staged->always,
|
|
[&](PeerId id) { return !current.always.contains(id); }
|
|
) || ranges::any_of(
|
|
current.never,
|
|
[&](PeerId id) { return !staged->never.contains(id); });
|
|
const auto exceptionsAllowLess = ranges::any_of(
|
|
staged->never,
|
|
[&](PeerId id) { return !current.never.contains(id); }
|
|
) || ranges::any_of(
|
|
current.always,
|
|
[&](PeerId id) { return !staged->always.contains(id); });
|
|
|
|
if (changed) {
|
|
for (const auto &[type, enabled] : values) {
|
|
const auto value = enabled ? limitByType(type) : 0;
|
|
settings->setBytesLimit(_source, type, value);
|
|
}
|
|
}
|
|
if (hiddenChanged) {
|
|
for (const auto type : kHidden) {
|
|
const auto now = settings->bytesLimit(_source, type);
|
|
if (now > 0) {
|
|
settings->setBytesLimit(
|
|
_source,
|
|
type,
|
|
limitByType(type));
|
|
}
|
|
}
|
|
}
|
|
if (exceptionsChanged) {
|
|
for (const auto &peerId : current.always) {
|
|
if (!staged->always.contains(peerId)) {
|
|
settings->setPeerOverride(peerId, Override::Default);
|
|
}
|
|
}
|
|
for (const auto &peerId : current.never) {
|
|
if (!staged->never.contains(peerId)) {
|
|
settings->setPeerOverride(peerId, Override::Default);
|
|
}
|
|
}
|
|
for (const auto &peerId : staged->always) {
|
|
settings->setPeerOverride(peerId, Override::ForceAllow);
|
|
}
|
|
for (const auto &peerId : staged->never) {
|
|
settings->setPeerOverride(peerId, Override::ForceDeny);
|
|
}
|
|
}
|
|
if (changed || hiddenChanged || exceptionsChanged) {
|
|
_session->saveSettingsDelayed();
|
|
}
|
|
if (allowMoreTypes.contains(Type::Photo) || exceptionsAllowMore) {
|
|
_session->data().photoLoadSettingsChanged();
|
|
}
|
|
if (ranges::any_of(allowMoreTypes, _1 != Type::Photo)
|
|
|| exceptionsAllowMore) {
|
|
_session->data().documentLoadSettingsChanged();
|
|
}
|
|
if (less || exceptionsAllowLess) {
|
|
_session->data().checkPlayingAnimations();
|
|
}
|
|
closeBox();
|
|
});
|
|
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
|
|
|
setDimensionsToContent(st::boxWidth, content);
|
|
}
|