Move manage filters to a Settings section.

This commit is contained in:
John Preston 2020-03-24 12:58:47 +04:00
parent 568325f201
commit e8bf5bb5ce
14 changed files with 238 additions and 191 deletions

View File

@ -153,8 +153,6 @@ PRIVATE
boxes/filters/edit_filter_box.h
boxes/filters/edit_filter_chats_list.cpp
boxes/filters/edit_filter_chats_list.h
boxes/filters/manage_filters_box.cpp
boxes/filters/manage_filters_box.h
boxes/peers/add_participants_box.cpp
boxes/peers/add_participants_box.h
boxes/peers/edit_contact_box.cpp
@ -842,6 +840,8 @@ PRIVATE
settings/settings_codes.h
settings/settings_common.cpp
settings/settings_common.h
settings/settings_folders.cpp
settings/settings_folders.h
settings/settings_information.cpp
settings/settings_information.h
settings/settings_intro.cpp

View File

@ -1,49 +0,0 @@
/*
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
*/
#pragma once
#include "data/data_chat_filters.h"
class ApiWrap;
namespace Ui {
class GenericBox;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Window
class ManageFiltersPrepare final {
public:
explicit ManageFiltersPrepare(
not_null<Window::SessionController*> window);
~ManageFiltersPrepare();
void showBox();
private:
struct Suggested {
Data::ChatFilter filter;
QString description;
};
void showBoxWithSuggested();
static void SetupBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
const std::vector<Suggested> &suggested);
const not_null<Window::SessionController*> _window;
const not_null<ApiWrap*> _api;
mtpRequestId _requestId = 0;
std::vector<Suggested> _suggested;
crl::time _suggestedLastReceived = 0;
};

View File

@ -19,6 +19,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
namespace Data {
namespace {
constexpr auto kRefreshSuggestedTimeout = 7200 * crl::time(1000);
} // namespace
ChatFilter::ChatFilter(
FilterId id,
@ -500,5 +505,50 @@ auto ChatFilters::refreshHistoryRequests() const
return _refreshHistoryRequests.events();
}
void ChatFilters::requestSuggested() {
if (_suggestedRequestId) {
return;
}
if (_suggestedLastReceived > 0
&& crl::now() - _suggestedLastReceived < kRefreshSuggestedTimeout) {
return;
}
const auto api = &_owner->session().api();
_suggestedRequestId = api->request(MTPmessages_GetSuggestedDialogFilters(
)).done([=](const MTPVector<MTPDialogFilterSuggested> &data) {
_suggestedRequestId = 0;
_suggestedLastReceived = crl::now();
_suggested = ranges::view::all(
data.v
) | ranges::view::transform([&](const MTPDialogFilterSuggested &f) {
return f.match([&](const MTPDdialogFilterSuggested &data) {
return SuggestedFilter{
Data::ChatFilter::FromTL(data.vfilter(), _owner),
qs(data.vdescription())
};
});
}) | ranges::to_vector;
_suggestedUpdated.fire({});
}).fail([=](const RPCError &error) {
_suggestedRequestId = 0;
_suggestedLastReceived = crl::now() + kRefreshSuggestedTimeout / 2;
_suggestedUpdated.fire({});
}).send();
}
bool ChatFilters::suggestedLoaded() const {
return (_suggestedLastReceived > 0);
}
const std::vector<SuggestedFilter> &ChatFilters::suggestedFilters() const {
return _suggested;
}
rpl::producer<> ChatFilters::suggestedUpdated() const {
return _suggestedUpdated.events();
}
} // namespace Data

View File

@ -85,6 +85,11 @@ inline bool operator!=(const ChatFilter &a, const ChatFilter &b) {
return !(a == b);
}
struct SuggestedFilter {
ChatFilter filter;
QString description;
};
class ChatFilters final {
public:
explicit ChatFilters(not_null<Session*> owner);
@ -112,6 +117,12 @@ public:
[[nodiscard]] bool archiveNeeded() const;
void requestSuggested();
[[nodiscard]] bool suggestedLoaded() const;
[[nodiscard]] auto suggestedFilters() const
-> const std::vector<SuggestedFilter> &;
[[nodiscard]] rpl::producer<> suggestedUpdated() const;
private:
void load(bool force);
bool applyOrder(const QVector<MTPint> &order);
@ -130,6 +141,11 @@ private:
mtpRequestId _saveOrderAfterId = 0;
bool _loaded = false;
mtpRequestId _suggestedRequestId = 0;
std::vector<SuggestedFilter> _suggested;
rpl::event_stream<> _suggestedUpdated;
crl::time _suggestedLastReceived = 0;
};
} // namespace Data

View File

@ -624,6 +624,8 @@ rpl::producer<QString> TitleValue(
return tr::lng_settings_advanced();
case Section::SettingsType::Chat:
return tr::lng_settings_section_chat_settings();
case Section::SettingsType::Folders:
return tr::lng_filters_title();
case Section::SettingsType::Calls:
return tr::lng_settings_section_call_settings();
}

View File

@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_memento.h"
#include "info/info_controller.h"
#include "settings/settings_common.h"
#include "boxes/filters/manage_filters_box.h"
#include "ui/ui_utility.h"
namespace Info {
@ -44,20 +43,14 @@ Widget::Widget(
: ContentWidget(parent, controller)
, _self(controller->key().settingsSelf())
, _type(controller->section().settingsType())
, _inner(setInnerWidget(::Settings::CreateSection(
_type,
this,
controller->parentController())))
, _manageFilters(
std::make_unique<ManageFiltersPrepare>(
controller->parentController())) {
, _inner(setInnerWidget(
::Settings::CreateSection(
_type,
this,
controller->parentController()))) {
_inner->sectionShowOther(
) | rpl::start_with_next([=](Type type) {
if (type == Type::Folders) {
_manageFilters->showBox();
} else {
controller->showSettings(type);
}
controller->showSettings(type);
}, _inner->lifetime());
controller->setCanSaveChanges(_inner->sectionCanSaveChanges());

View File

@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_content_widget.h"
#include "info/info_controller.h"
class ManageFiltersPrepare;
namespace Settings {
class Section;
} // namespace Settings
@ -80,7 +78,6 @@ private:
Type _type = Type();
not_null<::Settings::Section*> _inner;
std::unique_ptr<ManageFiltersPrepare> _manageFilters;
};

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_main.h"
#include "settings/settings_notifications.h"
#include "settings/settings_privacy_security.h"
#include "settings/settings_folders.h"
#include "settings/settings_calls.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/vertical_layout.h"
@ -46,6 +47,8 @@ object_ptr<Section> CreateSection(
return object_ptr<PrivacySecurity>(parent, controller);
case Type::Advanced:
return object_ptr<Advanced>(parent, controller);
case Type::Folders:
return object_ptr<Folders>(parent, controller);
case Type::Chat:
return object_ptr<Chat>(parent, controller);
case Type::Calls:

View File

@ -5,12 +5,13 @@ 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/filters/manage_filters_box.h"
#include "settings/settings_folders.h"
#include "boxes/filters/edit_filter_box.h"
#include "data/data_session.h"
#include "data/data_folder.h"
#include "data/data_peer.h"
#include "data/data_chat_filters.h"
#include "history/history.h"
#include "main/main_session.h"
#include "window/window_session_controller.h"
@ -25,19 +26,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_common.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "app.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_window.h"
namespace Settings {
namespace {
constexpr auto kRefreshSuggestedTimeout = 7200 * crl::time(1000);
constexpr auto kFiltersLimit = 10;
using namespace Settings;
using Flag = Data::ChatFilter::Flag;
using Flags = Data::ChatFilter::Flags;
@ -92,6 +93,13 @@ private:
};
struct FilterRow {
not_null<FilterRowButton*> button;
Data::ChatFilter filter;
bool removed = false;
bool added = false;
};
[[nodiscard]] int CountFilterChats(
not_null<Main::Session*> session,
const Data::ChatFilter &filter) {
@ -274,78 +282,17 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
_status);
}
} // namespace
[[nodiscard]] Fn<void()> SetupFoldersContent(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container) {
auto &lifetime = container->lifetime();
ManageFiltersPrepare::ManageFiltersPrepare(
not_null<Window::SessionController*> window)
: _window(window)
, _api(&_window->session().api()) {
}
const auto session = &controller->session();
AddSkip(container, st::settingsSectionSkip);
AddSubsectionTitle(container, tr::lng_filters_subtitle());
ManageFiltersPrepare::~ManageFiltersPrepare() {
if (_requestId) {
_api->request(_requestId).cancel();
}
}
void ManageFiltersPrepare::showBox() {
if (_requestId) {
return;
}
if (_suggestedLastReceived > 0
&& crl::now() - _suggestedLastReceived < kRefreshSuggestedTimeout) {
showBoxWithSuggested();
return;
}
_requestId = _api->request(MTPmessages_GetSuggestedDialogFilters(
)).done([=](const MTPVector<MTPDialogFilterSuggested> &data) {
_requestId = 0;
_suggestedLastReceived = crl::now();
const auto owner = &_api->session().data();
_suggested = ranges::view::all(
data.v
) | ranges::view::transform([&](const MTPDialogFilterSuggested &f) {
return f.match([&](const MTPDdialogFilterSuggested &data) {
return Suggested{
Data::ChatFilter::FromTL(data.vfilter(), owner),
qs(data.vdescription())
};
});
}) | ranges::to_vector;
showBoxWithSuggested();
}).fail([=](const RPCError &error) {
_requestId = 0;
_suggestedLastReceived = crl::now() + kRefreshSuggestedTimeout / 2;
showBoxWithSuggested();
}).send();
}
void ManageFiltersPrepare::showBoxWithSuggested() {
_window->window().show(Box(SetupBox, _window, _suggested));
}
void ManageFiltersPrepare::SetupBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
const std::vector<Suggested> &suggestions) {
box->setTitle(tr::lng_filters_title());
struct FilterRow {
not_null<FilterRowButton*> button;
Data::ChatFilter filter;
bool removed = false;
bool added = false;
};
const auto session = &window->session();
const auto content = box->verticalLayout();
AddSubsectionTitle(content, tr::lng_filters_subtitle());
const auto rows = box->lifetime().make_state<std::vector<FilterRow>>();
const auto rowsCount = box->lifetime().make_state<rpl::variable<int>>();
const auto rows = lifetime.make_state<std::vector<FilterRow>>();
const auto rowsCount = lifetime.make_state<rpl::variable<int>>();
const auto find = [=](not_null<FilterRowButton*> button) {
const auto i = ranges::find(*rows, button, &FilterRow::button);
Assert(i != end(*rows));
@ -356,10 +303,10 @@ void ManageFiltersPrepare::SetupBox(
if (rows->size() < kFiltersLimit + removed) {
return false;
}
window->window().showToast(tr::lng_filters_limit(tr::now));
controller->window().showToast(tr::lng_filters_limit(tr::now));
return true;
};
const auto wrap = content->add(object_ptr<Ui::VerticalLayout>(content));
const auto wrap = container->add(object_ptr<Ui::VerticalLayout>(container));
const auto addFilter = [=](const Data::ChatFilter &filter) {
const auto button = wrap->add(
object_ptr<FilterRowButton>(wrap, session, filter));
@ -385,16 +332,16 @@ void ManageFiltersPrepare::SetupBox(
find(button)->filter = result;
button->updateData(result);
};
window->window().show(Box(
controller->window().show(Box(
EditFilterBox,
window,
controller,
found->filter,
crl::guard(button, doneCallback)));
});
rows->push_back({ button, filter });
*rowsCount = rows->size();
wrap->resizeToWidth(content->width());
wrap->resizeToWidth(container->width());
};
const auto &list = session->data().chatsFilters().list();
for (const auto &filter : list) {
@ -402,7 +349,7 @@ void ManageFiltersPrepare::SetupBox(
}
AddButton(
content,
container,
tr::lng_filters_create() | Ui::Text::ToUpper(),
st::settingsUpdate
)->setClickedCallback([=] {
@ -412,54 +359,69 @@ void ManageFiltersPrepare::SetupBox(
const auto doneCallback = [=](const Data::ChatFilter &result) {
addFilter(result);
};
window->window().show(Box(
controller->window().show(Box(
EditFilterBox,
window,
controller,
Data::ChatFilter(),
crl::guard(box, doneCallback)));
crl::guard(container, doneCallback)));
});
AddSkip(content);
const auto emptyAbout = content->add(
AddSkip(container);
const auto emptyAbout = container->add(
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
content,
container,
object_ptr<Ui::FlatLabel>(
content,
container,
tr::lng_filters_about(),
st::boxDividerLabel),
st::settingsDividerLabelPadding)
)->setDuration(0);
const auto nonEmptyAbout = content->add(
const auto nonEmptyAbout = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
content,
object_ptr<Ui::VerticalLayout>(content))
container,
object_ptr<Ui::VerticalLayout>(container))
)->setDuration(0);
const auto aboutRows = nonEmptyAbout->entity();
AddDividerText(aboutRows, tr::lng_filters_about());
AddSkip(aboutRows);
AddSubsectionTitle(aboutRows, tr::lng_filters_recommended());
const auto changed = box->lifetime().make_state<bool>();
const auto suggested = box->lifetime().make_state<rpl::variable<int>>();
for (const auto &suggestion : suggestions) {
const auto &filter = suggestion.filter;
if (ranges::contains(list, filter)) {
continue;
}
*suggested = suggested->current() + 1;
const auto button = aboutRows->add(object_ptr<FilterRowButton>(
aboutRows,
filter,
suggestion.description));
button->addRequests(
) | rpl::start_with_next([=] {
if (showLimitReached()) {
return;
const auto changed = lifetime.make_state<bool>();
const auto suggested = lifetime.make_state<rpl::variable<int>>();
rpl::single(
rpl::empty_value()
) | rpl::then(
session->data().chatsFilters().suggestedUpdated()
) | rpl::map([=] {
return session->data().chatsFilters().suggestedFilters();
}) | rpl::filter([=](const std::vector<Data::SuggestedFilter> &list) {
return !list.empty();
}) | rpl::take(
1
) | rpl::start_with_next([=](
const std::vector<Data::SuggestedFilter> &suggestions) {
for (const auto &suggestion : suggestions) {
const auto &filter = suggestion.filter;
if (ranges::contains(*rows, filter, &FilterRow::filter)) {
continue;
}
addFilter(filter);
*suggested = suggested->current() - 1;
delete button;
}, button->lifetime());
}
*suggested = suggested->current() + 1;
const auto button = aboutRows->add(object_ptr<FilterRowButton>(
aboutRows,
filter,
suggestion.description));
button->addRequests(
) | rpl::start_with_next([=] {
if (showLimitReached()) {
return;
}
addFilter(filter);
*suggested = suggested->current() - 1;
delete button;
}, button->lifetime());
}
aboutRows->resizeToWidth(container->width());
AddSkip(aboutRows, st::settingsSectionSkip);
}, aboutRows->lifetime());
using namespace rpl::mappers;
auto showSuggestions = rpl::combine(
@ -494,7 +456,7 @@ void ManageFiltersPrepare::SetupBox(
return result;
};
const auto save = [=] {
return [=] {
auto ids = prepareGoodIdsForNewFilters();
using Requests = std::vector<MTPmessages_UpdateDialogFilter>;
@ -546,8 +508,32 @@ void ManageFiltersPrepare::SetupBox(
if (!order.empty() && !addRequests.empty()) {
realFilters.saveOrder(order, previousId);
}
box->closeBox();
};
box->boxClosing() | rpl::start_with_next(save, box->lifetime());
box->addButton(tr::lng_about_done(), [=] { box->closeBox(); });
}
} // namespace
Folders::Folders(
QWidget *parent,
not_null<Window::SessionController*> controller)
: Section(parent) {
setupContent(controller);
}
Folders::~Folders() {
if (!App::quitting()) {
_save();
}
}
void Folders::setupContent(not_null<Window::SessionController*> controller) {
controller->session().data().chatsFilters().requestSuggested();
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
_save = SetupFoldersContent(controller, content);
Ui::ResizeFitChild(this, content);
}
} // namespace Settings

View File

@ -0,0 +1,29 @@
/*
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
*/
#pragma once
#include "settings/settings_common.h"
namespace Settings {
class Folders : public Section {
public:
Folders(
QWidget *parent,
not_null<Window::SessionController*> controller);
~Folders();
private:
void setupContent(not_null<Window::SessionController*> controller);
Fn<void()> _save;
};
} // namespace Settings

View File

@ -106,6 +106,9 @@ void SetupSections(
Type::Chat,
&st::settingsIconChat);
const auto preload = [=] {
controller->session().data().chatsFilters().requestSuggested();
};
const auto account = &controller->session().account();
const auto slided = container->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
@ -118,18 +121,30 @@ void SetupSections(
if (!controller->session().data().chatsFilters().list().empty()
|| Global::DialogsFiltersEnabled()) {
slided->show(anim::type::instant);
preload();
} else {
const auto enabled = [=] {
return account->appConfig().get<bool>(
const auto result = account->appConfig().get<bool>(
"dialog_filters_enabled",
false);
if (result) {
preload();
}
return result;
};
const auto preloadIfEnabled = [=](bool enabled) {
if (enabled) {
preload();
}
};
slided->toggleOn(
rpl::single(
rpl::empty_value()
) | rpl::then(
account->appConfig().refreshed()
) | rpl::map(enabled));
) | rpl::map(
enabled
) | rpl::before_next(preloadIfEnabled));
}
slided->entity()->setClickedCallback([=] {
showOther(Type::Folders);

View File

@ -13,10 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_chat_filters.h"
#include "boxes/filters/manage_filters_box.h"
#include "lang/lang_keys.h"
#include "ui/filter_icons.h"
#include "ui/wrap/vertical_layout_reorder.h"
#include "settings/settings_common.h"
#include "api/api_chat_filters.h"
#include "styles/style_widgets.h"
#include "styles/style_window.h"
@ -28,7 +28,6 @@ FiltersMenu::FiltersMenu(
not_null<SessionController*> session)
: _session(session)
, _parent(parent)
, _manage(std::make_unique<ManageFiltersPrepare>(_session))
, _outer(_parent)
, _menu(&_outer, QString(), st::windowFiltersMainMenu)
, _scroll(&_outer)
@ -191,7 +190,17 @@ base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareButton(
} else if (id >= 0) {
_session->setActiveChatsFilter(id);
} else {
_manage->showBox();
const auto filters = &_session->session().data().chatsFilters();
if (filters->suggestedLoaded()) {
_session->showSettings(Settings::Type::Folders);
} else if (!_waitingSuggested) {
_waitingSuggested = true;
filters->requestSuggested();
filters->suggestedUpdated(
) | rpl::take(1) | rpl::start_with_next([=] {
_session->showSettings(Settings::Type::Folders);
}, _outer.lifetime());
}
}
});
return button;

View File

@ -11,8 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/vertical_layout.h"
class ManageFiltersPrepare;
namespace Ui {
class VerticalLayoutReorder;
enum class FilterIcon : uchar;
@ -45,7 +43,6 @@ private:
const not_null<SessionController*> _session;
const not_null<Ui::RpWidget*> _parent;
std::unique_ptr<ManageFiltersPrepare> _manage;
Ui::RpWidget _outer;
Ui::SideBarButton _menu;
Ui::ScrollArea _scroll;
@ -58,6 +55,7 @@ private:
FilterId _activeFilterId = 0;
int _reordering = 0;
bool _ignoreRefresh = false;
bool _waitingSuggested = false;
};

View File

@ -91,8 +91,6 @@ void SessionNavigation::showPeerInfo(
void SessionNavigation::showSettings(
Settings::Type type,
const SectionShow &params) {
Expects(type != Settings::Type::Folders);
showSection(
Info::Memento(
Info::Settings::Tag{ _session->user() },