From 292e5bc3f7af8e54a676ed0389d104b2cdd71667 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 5 Apr 2023 12:10:24 +0400 Subject: [PATCH] Improve filters save-on-demand. --- Telegram/Resources/langs/lang.strings | 1 + .../boxes/filters/edit_filter_box.cpp | 68 ++++++++++++------- .../SourceFiles/data/data_chat_filters.cpp | 46 +++++++++++-- Telegram/SourceFiles/data/data_chat_filters.h | 6 ++ .../SourceFiles/settings/settings_folders.cpp | 46 +++++++++++-- 5 files changed, 129 insertions(+), 38 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 750f0c087..771f951f9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp index 87221ff0e..5a63062c1 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp @@ -540,27 +540,18 @@ void EditFilterBox( Fn 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 rules; rpl::variable> links; rpl::variable hasLinks; rpl::variable chatlist; + rpl::variable creating; }; const auto owner = &window->session().data(); const auto state = box->lifetime().make_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( @@ -592,7 +609,12 @@ void EditFilterBox( const auto nameEditing = box->lifetime().make_state( 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 { 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(); }); } diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp index c822cc6af..26c6b6287 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.cpp +++ b/Telegram/SourceFiles/data/data_chat_filters.cpp @@ -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(); @@ -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 &order) { @@ -767,6 +797,10 @@ rpl::producer<> ChatFilters::changed() const { return _listChanged.events(); } +rpl::producer ChatFilters::isChatlistChanged() const { + return _isChatlistChanged.events(); +} + bool ChatFilters::loadNextExceptions(bool chatsListLoaded) { if (_exceptionsLoadRequestId) { return true; diff --git a/Telegram/SourceFiles/data/data_chat_filters.h b/Telegram/SourceFiles/data/data_chat_filters.h index 6dc5e6384..987d55ebe 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.h +++ b/Telegram/SourceFiles/data/data_chat_filters.h @@ -54,6 +54,10 @@ public: base::flat_set> 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 &list() const; [[nodiscard]] rpl::producer<> changed() const; + [[nodiscard]] rpl::producer isChatlistChanged() const; [[nodiscard]] bool loaded() const; [[nodiscard]] bool has() const; @@ -195,6 +200,7 @@ private: std::vector _list; base::flat_map> _chatsLists; rpl::event_stream<> _listChanged; + rpl::event_stream _isChatlistChanged; mtpRequestId _loadRequestId = 0; mtpRequestId _saveOrderRequestId = 0; mtpRequestId _saveOrderAfterId = 0; diff --git a/Telegram/SourceFiles/settings/settings_folders.cpp b/Telegram/SourceFiles/settings/settings_folders.cpp index dd3d35784..6e479d58f 100644 --- a/Telegram/SourceFiles/settings/settings_folders.cpp +++ b/Telegram/SourceFiles/settings/settings_folders.cpp @@ -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 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(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 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,