Update API: support "All Chats" reordering.

This commit is contained in:
John Preston 2022-05-17 13:24:35 +04:00
parent 73bacfc650
commit fe91887ea2
13 changed files with 113 additions and 36 deletions

View file

@ -1183,6 +1183,7 @@ bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl;
payments.bankCardData#3e24e573 title:string open_urls:Vector<BankCardOpenUrl> = payments.BankCardData;
dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
dialogFilterDefault#363293ae = DialogFilter;
dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
@ -1786,6 +1787,7 @@ payments.getSavedInfo#227d824b = payments.SavedInfo;
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice;
payments.assignAppStoreTransaction#6299a12f transaction_id:string = Updates;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true videos:flags.4?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;

View file

@ -57,6 +57,8 @@ void ChangeFilterById(
FilterId filterId,
not_null<History*> history,
bool add) {
Expects(filterId != 0);
const auto list = history->owner().chatsFilters().list();
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
if (i != end(list)) {
@ -99,7 +101,7 @@ ChooseFilterValidator::ChooseFilterValidator(not_null<History*> history)
bool ChooseFilterValidator::canAdd() const {
for (const auto &filter : _history->owner().chatsFilters().list()) {
if (!filter.contains(_history)) {
if (filter.id() && !filter.contains(_history)) {
return true;
}
}
@ -107,6 +109,8 @@ bool ChooseFilterValidator::canAdd() const {
}
bool ChooseFilterValidator::canRemove(FilterId filterId) const {
Expects(filterId != 0);
const auto list = _history->owner().chatsFilters().list();
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
if (i != end(list)) {
@ -118,6 +122,8 @@ bool ChooseFilterValidator::canRemove(FilterId filterId) const {
}
bool ChooseFilterValidator::limitReached(FilterId filterId) const {
Expects(filterId != 0);
const auto list = _history->owner().chatsFilters().list();
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
const auto limit = _history->owner().pinnedChatsLimit(nullptr, filterId);
@ -142,6 +148,10 @@ void FillChooseFilterMenu(
const auto validator = ChooseFilterValidator(history);
for (const auto &filter : history->owner().chatsFilters().list()) {
const auto id = filter.id();
if (!id) {
continue;
}
const auto contains = filter.contains(history);
const auto action = menu->addAction(filter.title(), [=] {
if (filter.contains(history)) {

View file

@ -668,6 +668,8 @@ void EditFilterBox(
void EditExistingFilter(
not_null<Window::SessionController*> window,
FilterId id) {
Expects(id != 0);
const auto session = &window->session();
const auto &list = session->data().chatsFilters().list();
const auto i = ranges::find(list, id, &Data::ChatFilter::id);

View file

@ -107,6 +107,8 @@ ChatFilter ChatFilter::FromTL(
std::move(list),
std::move(pinned),
{ never.begin(), never.end() });
}, [](const MTPDdialogFilterDefault &d) {
return ChatFilter();
});
}
@ -220,6 +222,7 @@ bool ChatFilter::contains(not_null<History*> history) const {
}
ChatFilters::ChatFilters(not_null<Session*> owner) : _owner(owner) {
_list.emplace_back();
crl::on_main(&owner->session(), [=] { load(); });
}
@ -295,6 +298,9 @@ void ChatFilters::received(const QVector<MTPDialogFilter> &list) {
applyRemove(position);
changed = true;
}
if (!ranges::contains(begin(_list), end(_list), 0, &ChatFilter::id)) {
_list.insert(begin(_list), ChatFilter());
}
if (changed || !_loaded) {
_loaded = true;
_listChanged.fire({});
@ -352,6 +358,16 @@ void ChatFilters::remove(FilterId id) {
_listChanged.fire({});
}
void ChatFilters::moveAllToFront() {
const auto i = ranges::find(_list, FilterId(), &ChatFilter::id);
if (!_list.empty() && i == begin(_list)) {
return;
} else if (i != end(_list)) {
_list.erase(i);
}
_list.insert(begin(_list), ChatFilter());
}
void ChatFilters::applyRemove(int position) {
Expects(position >= 0 && position < _list.size());
@ -518,6 +534,10 @@ const std::vector<ChatFilter> &ChatFilters::list() const {
return _list;
}
bool ChatFilters::has() const {
return _list.size() > 1;
}
rpl::producer<> ChatFilters::changed() const {
return _listChanged.events();
}

View file

@ -99,8 +99,10 @@ public:
void apply(const MTPUpdate &update);
void set(ChatFilter filter);
void remove(FilterId id);
void moveAllToFront();
[[nodiscard]] const std::vector<ChatFilter> &list() const;
[[nodiscard]] rpl::producer<> changed() const;
[[nodiscard]] bool has() const;
bool loadNextExceptions(bool chatsListLoaded);

View file

@ -268,7 +268,7 @@ Session::Session(not_null<Main::Session*> session)
_chatsFilters->changed(
) | rpl::start_with_next([=] {
const auto enabled = !_chatsFilters->list().empty();
const auto enabled = _chatsFilters->has();
if (enabled != session->settings().dialogsFiltersEnabled()) {
session->settings().setDialogsFiltersEnabled(enabled);
session->saveSettingsDelayed();

View file

@ -2706,7 +2706,7 @@ bool InnerWidget::chooseCollapsedRow() {
}
void InnerWidget::switchToFilter(FilterId filterId) {
const auto found = ranges::contains(
const auto found = filterId && ranges::contains(
session().data().chatsFilters().list(),
filterId,
&Data::ChatFilter::id);
@ -3204,16 +3204,16 @@ void InnerWidget::setupShortcuts() {
return false;
});
const auto filters = &session().data().chatsFilters().list();
if (const auto filtersCount = int(filters->size())) {
if (session().data().chatsFilters().has()) {
const auto filters = &session().data().chatsFilters().list();
const auto filtersCount = int(filters->size());
auto &&folders = ranges::views::zip(
Shortcuts::kShowFolder,
ranges::views::ints(0, ranges::unreachable));
for (const auto [command, index] : folders) {
const auto select = (command == Command::ShowFolderLast)
? filtersCount
: std::clamp(index, 0, filtersCount);
: std::clamp(index, 0, int(filtersCount));
request->check(command) && request->handle([=] {
if (select <= filtersCount) {
_controller->setActiveChatsFilter((select > 0)
@ -3256,15 +3256,16 @@ void InnerWidget::setupShortcuts() {
const auto nearFolder = [=](bool isNext) {
const auto id = _controller->activeChatsFilterCurrent();
const auto list = &session().data().chatsFilters().list();
const auto index = (id != 0)
? int(ranges::find(*list, id, &Data::ChatFilter::id)
- begin(*list))
: -1;
const auto index = int(ranges::find(
*list,
id,
&Data::ChatFilter::id
) - begin(*list));
if (index == list->size() && id != 0) {
return false;
}
const auto changed = index + (isNext ? 1 : -1);
if (changed >= int(list->size()) || changed < -1) {
if (changed >= int(list->size()) || changed < 0) {
return false;
}
_controller->setActiveChatsFilter((changed >= 0)

View file

@ -424,7 +424,9 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
};
const auto &list = session->data().chatsFilters().list();
for (const auto &filter : list) {
addFilter(filter);
if (filter.id()) {
addFilter(filter);
}
}
AddButton(

View file

@ -289,7 +289,7 @@ void SetupSections(
st::settingsButton,
{ &st::settingsIconFolders, kIconDarkBlue }))
)->setDuration(0);
if (!controller->session().data().chatsFilters().list().empty()
if (controller->session().data().chatsFilters().has()
|| controller->session().settings().dialogsFiltersEnabled()) {
slided->show(anim::type::instant);
preload();

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_chat_filters.h"
#include "data/data_folder.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "ui/filter_icons.h"
#include "ui/wrap/vertical_layout_reorder.h"
@ -103,9 +104,16 @@ void FiltersMenu::setup() {
_container->move(0, 0);
}, _outer.lifetime());
auto premium = _session->session().user()->flagsValue(
) | rpl::filter([=](UserData::Flags::Change change) {
return (change.diff & UserDataFlag::Premium);
}) | rpl::map([=] {
return _session->session().user()->isPremium();
});
const auto filters = &_session->session().data().chatsFilters();
rpl::single(rpl::empty) | rpl::then(
filters->changed()
rpl::combine(
rpl::single(rpl::empty) | rpl::then(filters->changed()),
std::move(premium)
) | rpl::start_with_next([=] {
refresh();
}, _outer.lifetime());
@ -118,7 +126,7 @@ void FiltersMenu::setup() {
const auto i = _filters.find(_activeFilterId);
if (i != end(_filters)) {
i->second->setActive(false);
} else if (!_activeFilterId) {
} else if (!_activeFilterId && _all) {
_all->setActive(false);
}
_activeFilterId = id;
@ -126,7 +134,7 @@ void FiltersMenu::setup() {
if (j != end(_filters)) {
j->second->setActive(true);
scrollToButton(j->second);
} else if (!_activeFilterId) {
} else if (!_activeFilterId && _all) {
_all->setActive(true);
scrollToButton(_all);
}
@ -178,17 +186,24 @@ void FiltersMenu::scrollToButton(not_null<Ui::RpWidget*> widget) {
void FiltersMenu::refresh() {
const auto filters = &_session->session().data().chatsFilters();
if (filters->list().empty() || _ignoreRefresh) {
if (!filters->has() || _ignoreRefresh) {
return;
}
const auto oldTop = _scroll.scrollTop();
const auto reorderAll = premium();
if (!_list) {
setupList();
} else if (reorderAll && _all) {
_all = nullptr;
} else if (!reorderAll && !_all) {
_all = prepareAll();
}
_reorder->cancel();
auto now = base::flat_map<int, base::unique_qptr<Ui::SideBarButton>>();
for (const auto &filter : filters->list()) {
if (!reorderAll && !filter.id()) {
continue;
}
now.emplace(
filter.id(),
prepareButton(
@ -206,15 +221,14 @@ void FiltersMenu::refresh() {
// so we have to restore it.
_scroll.scrollToY(oldTop);
const auto i = _filters.find(_activeFilterId);
scrollToButton((i != end(_filters)) ? i->second : _all);
const auto button = ((i != end(_filters)) ? i->second : _all).get();
if (button) {
scrollToButton(button);
}
}
void FiltersMenu::setupList() {
_all = prepareButton(
_container,
0,
tr::lng_filters_all(tr::now),
Ui::FilterIcon::All);
_all = premium() ? nullptr : prepareAll();
_list = _container->add(object_ptr<Ui::VerticalLayout>(_container));
_setup = prepareButton(
_container,
@ -239,18 +253,32 @@ void FiltersMenu::setupList() {
}, _outer.lifetime());
}
bool FiltersMenu::premium() const {
return _session->session().user()->isPremium();
}
base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareAll() {
return prepareButton(_container, 0, {}, Ui::FilterIcon::All, true);
}
base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareButton(
not_null<Ui::VerticalLayout*> container,
FilterId id,
const QString &title,
Ui::FilterIcon icon) {
auto button = base::unique_qptr<Ui::SideBarButton>(container->add(
object_ptr<Ui::SideBarButton>(
container,
title,
st::windowFiltersButton)));
Ui::FilterIcon icon,
bool toBeginning) {
auto prepared = object_ptr<Ui::SideBarButton>(
container,
id ? title : tr::lng_filters_all(tr::now),
st::windowFiltersButton);
auto added = toBeginning
? container->insert(0, std::move(prepared))
: container->add(std::move(prepared));
auto button = base::unique_qptr<Ui::SideBarButton>(std::move(added));
const auto raw = button.get();
const auto &icons = Ui::LookupFilterIcon(icon);
const auto &icons = Ui::LookupFilterIcon(id
? icon
: Ui::FilterIcon::All);
raw->setIconOverride(icons.normal, icons.active);
if (id >= 0) {
UnreadStateValue(
@ -372,6 +400,13 @@ void FiltersMenu::applyReorder(
const auto filters = &_session->session().data().chatsFilters();
const auto &list = filters->list();
if (_all) {
if (list[0].id() != FilterId()) {
filters->moveAllToFront();
}
++oldPosition;
++newPosition;
}
Assert(oldPosition >= 0 && oldPosition < list.size());
Assert(newPosition >= 0 && newPosition < list.size());
const auto id = list[oldPosition].id();

View file

@ -41,11 +41,14 @@ private:
not_null<Ui::RpWidget*> widget,
int oldPosition,
int newPosition);
[[nodiscard]] bool premium() const;
[[nodiscard]] base::unique_qptr<Ui::SideBarButton> prepareAll();
[[nodiscard]] base::unique_qptr<Ui::SideBarButton> prepareButton(
not_null<Ui::VerticalLayout*> container,
FilterId id,
const QString &title,
Ui::FilterIcon icon);
Ui::FilterIcon icon,
bool toBeginning = false);
void setupMainMenuIcon();
void showMenu(QPoint position, FilterId id);
void showEditBox(FilterId id);

View file

@ -419,7 +419,7 @@ void Filler::addInfo() {
void Filler::addToggleFolder() {
const auto controller = _controller;
const auto history = _request.key.history();
if (!history || history->owner().chatsFilters().list().empty()) {
if (!history || !history->owner().chatsFilters().has()) {
return;
}
_addAction(PeerMenuCallback::Args{

View file

@ -764,7 +764,7 @@ void SessionController::toggleFiltersMenu(bool enabled) {
}
void SessionController::refreshFiltersMenu() {
toggleFiltersMenu(!session().data().chatsFilters().list().empty());
toggleFiltersMenu(session().data().chatsFilters().has());
}
rpl::producer<> SessionController::filtersMenuChanged() const {