Improve filters save-on-demand.

This commit is contained in:
John Preston 2023-04-05 12:10:24 +04:00
parent 06cf2b562f
commit 292e5bc3f7
5 changed files with 129 additions and 38 deletions

View File

@ -3560,6 +3560,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_filters_menu_add" = "Add to folder";
"lng_filters_toast_add" = "{chat} added to {folder} folder";
"lng_filters_toast_remove" = "{chat} removed from {folder} folder";
"lng_filters_shareable_status" = "shareable folder";
"lng_filters_delete_sure" = "Are you sure you want to delete this folder? This will also deactivate all the invite links used to share this folder.";
"lng_filters_link" = "Share Folder";

View File

@ -540,27 +540,18 @@ void EditFilterBox(
Fn<void(Data::ChatFilter)> next)> saveAnd) {
using namespace rpl::mappers;
const auto creating = filter.title().isEmpty();
box->setWidth(st::boxWideWidth);
box->setTitle(creating ? tr::lng_filters_new() : tr::lng_filters_edit());
box->setCloseByOutsideClick(false);
Data::AmPremiumValue(
&window->session()
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
struct State {
rpl::variable<Data::ChatFilter> rules;
rpl::variable<std::vector<Data::ChatFilterLink>> links;
rpl::variable<bool> hasLinks;
rpl::variable<bool> chatlist;
rpl::variable<bool> creating;
};
const auto owner = &window->session().data();
const auto state = box->lifetime().make_state<State>(State{
.rules = filter,
.chatlist = filter.chatlist(),
.creating = filter.title().isEmpty(),
});
state->links = owner->chatsFilters().chatlistLinks(filter.id()),
state->hasLinks = state->links.value() | rpl::map([=](const auto &v) {
@ -573,6 +564,32 @@ void EditFilterBox(
}
const auto data = &state->rules;
owner->chatsFilters().isChatlistChanged(
) | rpl::filter([=](FilterId id) {
return (id == data->current().id());
}) | rpl::start_with_next([=](FilterId id) {
const auto filters = &owner->chatsFilters();
const auto &list = filters->list();
const auto i = ranges::find(list, id, &Data::ChatFilter::id);
if (i == end(list)) {
return;
}
*data = data->current().withChatlist(i->chatlist(), i->hasMyLinks());
}, box->lifetime());
box->setWidth(st::boxWideWidth);
box->setTitle(rpl::conditional(
state->creating.value(),
tr::lng_filters_new(),
tr::lng_filters_edit()));
box->setCloseByOutsideClick(false);
Data::AmPremiumValue(
&window->session()
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
const auto content = box->verticalLayout();
const auto name = content->add(
object_ptr<Ui::InputField>(
@ -592,7 +609,12 @@ void EditFilterBox(
const auto nameEditing = box->lifetime().make_state<NameEditing>(
NameEditing{ name });
nameEditing->custom = !creating;
state->creating.value(
) | rpl::filter(!_1) | rpl::start_with_next([=] {
nameEditing->custom = true;
}, box->lifetime());
QObject::connect(name, &Ui::InputField::changed, [=] {
if (!nameEditing->settingDefault) {
nameEditing->custom = true;
@ -683,14 +705,6 @@ void EditFilterBox(
const auto collect = [=]() -> std::optional<Data::ChatFilter> {
const auto title = name->getLastText().trimmed();
const auto rules = data->current();
const auto result = Data::ChatFilter(
rules.id(),
title,
rules.iconEmoji(),
rules.flags(),
rules.always(),
rules.pinned(),
rules.never());
if (title.isEmpty()) {
name->showError();
box->scrollToY(0);
@ -704,7 +718,7 @@ void EditFilterBox(
window->window().showToast(tr::lng_filters_default(tr::now));
return {};
}
return result;
return rules.withTitle(title);
};
AddSubsectionTitle(
@ -758,8 +772,7 @@ void EditFilterBox(
return;
}
saveAnd(*result, crl::guard(box, [=](Data::ChatFilter updated) {
box->setTitle(tr::lng_filters_edit());
nameEditing->custom = true;
state->creating = false;
// Comparison of ChatFilter-s don't take id into account!
data->force_assign(updated);
@ -768,6 +781,7 @@ void EditFilterBox(
ExportFilterLink(id, shared, [=](Data::ChatFilterLink link) {
Expects(link.id == id);
*data = data->current().withChatlist(true, true);
window->show(ShowLinkBox(window, updated, link));
}, [=](QString error) {
if (error == u"CHATLISTS_TOO_MUCH"_q) {
@ -826,9 +840,11 @@ void EditFilterBox(
}
};
box->addButton(
creating ? tr::lng_filters_create_button() : tr::lng_settings_save(),
save);
box->addButton(rpl::conditional(
state->creating.value(),
tr::lng_filters_create_button(),
tr::lng_settings_save()
), save);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}

View File

@ -169,6 +169,27 @@ ChatFilter ChatFilter::withId(FilterId id) const {
return result;
}
ChatFilter ChatFilter::withTitle(const QString &title) const {
auto result = *this;
result._title = title;
return result;
}
ChatFilter ChatFilter::withChatlist(bool chatlist, bool hasMyLinks) const {
auto result = *this;
if (chatlist) {
result._flags |= Flag::Chatlist;
if (hasMyLinks) {
result._flags |= Flag::HasMyLinks;
} else {
result._flags &= ~Flag::HasMyLinks;
}
} else {
result._flags &= ~(Flag::Chatlist | Flag::HasMyLinks);
}
return result;
}
MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
auto always = _always;
auto pinned = QVector<MTPInputPeer>();
@ -582,16 +603,22 @@ void ChatFilters::applyRemove(int position) {
bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) {
Expects(filter.id() == updated.id());
using Flag = ChatFilter::Flag;
const auto id = filter.id();
const auto exceptionsChanged = filter.always() != updated.always();
const auto rulesMask = ~(Flag::Chatlist | Flag::HasMyLinks);
const auto rulesChanged = exceptionsChanged
|| (filter.flags() != updated.flags())
|| ((filter.flags() & rulesMask) != (updated.flags() & rulesMask))
|| (filter.never() != updated.never());
const auto pinnedChanged = (filter.pinned() != updated.pinned());
if (!rulesChanged
&& !pinnedChanged
&& filter.title() == updated.title()
&& filter.iconEmoji() == updated.iconEmoji()) {
const auto chatlistChanged = (filter.chatlist() != updated.chatlist())
|| (filter.hasMyLinks() != updated.hasMyLinks());
const auto listUpdated = rulesChanged
|| pinnedChanged
|| (filter.title() != updated.title())
|| (filter.iconEmoji() != updated.iconEmoji());
if (!listUpdated && !chatlistChanged) {
return false;
}
if (rulesChanged) {
@ -630,7 +657,10 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) {
const auto filterList = _owner->chatsFilters().chatsList(id);
filterList->pinned()->applyList(filter.pinned());
}
return true;
if (chatlistChanged) {
_isChatlistChanged.fire_copy(id);
}
return listUpdated;
}
bool ChatFilters::applyOrder(const QVector<MTPint> &order) {
@ -767,6 +797,10 @@ rpl::producer<> ChatFilters::changed() const {
return _listChanged.events();
}
rpl::producer<FilterId> ChatFilters::isChatlistChanged() const {
return _isChatlistChanged.events();
}
bool ChatFilters::loadNextExceptions(bool chatsListLoaded) {
if (_exceptionsLoadRequestId) {
return true;

View File

@ -54,6 +54,10 @@ public:
base::flat_set<not_null<History*>> never);
[[nodiscard]] ChatFilter withId(FilterId id) const;
[[nodiscard]] ChatFilter withTitle(const QString &title) const;
[[nodiscard]] ChatFilter withChatlist(
bool chatlist,
bool hasMyLinks) const;
[[nodiscard]] static ChatFilter FromTL(
const MTPDialogFilter &data,
@ -126,6 +130,7 @@ public:
void moveAllToFront();
[[nodiscard]] const std::vector<ChatFilter> &list() const;
[[nodiscard]] rpl::producer<> changed() const;
[[nodiscard]] rpl::producer<FilterId> isChatlistChanged() const;
[[nodiscard]] bool loaded() const;
[[nodiscard]] bool has() const;
@ -195,6 +200,7 @@ private:
std::vector<ChatFilter> _list;
base::flat_map<FilterId, std::unique_ptr<Dialogs::MainList>> _chatsLists;
rpl::event_stream<> _listChanged;
rpl::event_stream<FilterId> _isChatlistChanged;
mtpRequestId _loadRequestId = 0;
mtpRequestId _saveOrderRequestId = 0;
mtpRequestId _saveOrderAfterId = 0;

View File

@ -159,7 +159,9 @@ struct FilterRow {
? tr::lng_filters_chats_count(tr::now, lt_count_short, count)
: tr::lng_filters_no_chats(tr::now);
return filter.chatlist()
? result + QString::fromUtf8(" \xE2\x80\xA2 shareable folder")
? (result
+ QString::fromUtf8(" \xE2\x80\xA2 ")
+ tr::lng_filters_shareable_status(tr::now))
: result;
}
@ -468,9 +470,7 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
const auto saveAnd = [=](
const Data::ChatFilter &data,
Fn<void(Data::ChatFilter)> next) {
const auto found = find(button);
found->filter = data;
button->updateData(data);
doneCallback(data);
state->save(button, next);
};
controller->window().show(Box(
@ -526,6 +526,21 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
}
}
session->data().chatsFilters().isChatlistChanged(
) | rpl::start_with_next([=](FilterId id) {
const auto filters = &session->data().chatsFilters();
const auto &list = filters->list();
const auto i = ranges::find(list, id, &Data::ChatFilter::id);
const auto j = ranges::find(state->rows, id, [](const auto &row) {
return row.filter.id();
});
if (i == end(list) || j == end(state->rows)) {
return;
}
j->filter = j->filter.withChatlist(i->chatlist(), i->hasMyLinks());
j->button->updateCount(j->filter);
}, container->lifetime());
AddButton(
container,
tr::lng_filters_create(),
@ -535,13 +550,20 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
if (showLimitReached()) {
return;
}
const auto created = std::make_shared<FilterRowButton*>(nullptr);
const auto doneCallback = [=](const Data::ChatFilter &result) {
addFilter(result);
if (const auto button = *created) {
find(button)->filter = result;
button->updateData(result);
} else {
*created = addFilter(result);
}
};
const auto saveAnd = [=](
const Data::ChatFilter &data,
Fn<void(Data::ChatFilter)> next) {
state->save(addFilter(data), next);
doneCallback(data);
state->save(*created, next);
};
controller->window().show(Box(
EditFilterBox,
@ -715,6 +737,18 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
}
order.insert(order.begin() + position, FilterId(0));
}
if (next) {
// We're not closing the layer yet, so delete removed rows.
for (auto i = state->rows.begin(); i != state->rows.end();) {
if (i->removed) {
const auto button = i->button;
i = state->rows.erase(i);
delete button;
} else {
++i;
}
}
}
crl::on_main(session, [
session,
next,