mirror of https://github.com/procxx/kepka.git
Support pinned locally in filters.
This commit is contained in:
parent
483d4e5a4e
commit
e27a8fe058
|
@ -484,7 +484,9 @@ void ApiWrap::applyUpdates(
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::savePinnedOrder(Data::Folder *folder) {
|
void ApiWrap::savePinnedOrder(Data::Folder *folder) {
|
||||||
const auto &order = _session->data().pinnedChatsOrder(folder);
|
const auto &order = _session->data().pinnedChatsOrder(
|
||||||
|
folder,
|
||||||
|
FilterId());
|
||||||
const auto input = [](const Dialogs::Key &key) {
|
const auto input = [](const Dialogs::Key &key) {
|
||||||
if (const auto history = key.history()) {
|
if (const auto history = key.history()) {
|
||||||
return MTP_inputDialogPeer(history->peer->input);
|
return MTP_inputDialogPeer(history->peer->input);
|
||||||
|
@ -513,7 +515,7 @@ void ApiWrap::toggleHistoryArchived(
|
||||||
if (const auto already = _historyArchivedRequests.take(history)) {
|
if (const auto already = _historyArchivedRequests.take(history)) {
|
||||||
request(already->first).cancel();
|
request(already->first).cancel();
|
||||||
}
|
}
|
||||||
const auto isPinned = history->isPinnedDialog();
|
const auto isPinned = history->isPinnedDialog(0);
|
||||||
const auto archiveId = Data::Folder::kId;
|
const auto archiveId = Data::Folder::kId;
|
||||||
const auto requestId = request(MTPfolders_EditPeerFolders(
|
const auto requestId = request(MTPfolders_EditPeerFolders(
|
||||||
MTP_vector<MTPInputFolderPeer>(
|
MTP_vector<MTPInputFolderPeer>(
|
||||||
|
@ -980,7 +982,7 @@ void ApiWrap::requestPinnedDialogs(Data::Folder *folder) {
|
||||||
result.match([&](const MTPDmessages_peerDialogs &data) {
|
result.match([&](const MTPDmessages_peerDialogs &data) {
|
||||||
_session->data().processUsers(data.vusers());
|
_session->data().processUsers(data.vusers());
|
||||||
_session->data().processChats(data.vchats());
|
_session->data().processChats(data.vchats());
|
||||||
_session->data().clearPinnedChats(folder);
|
_session->data().clearPinnedChats(folder, FilterId());
|
||||||
_session->data().applyDialogs(
|
_session->data().applyDialogs(
|
||||||
folder,
|
folder,
|
||||||
data.vmessages().v,
|
data.vmessages().v,
|
||||||
|
|
|
@ -106,20 +106,24 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
|
||||||
data->title(),
|
data->title(),
|
||||||
(data->flags() & ~flag),
|
(data->flags() & ~flag),
|
||||||
data->always(),
|
data->always(),
|
||||||
|
data->pinned(),
|
||||||
data->never());
|
data->never());
|
||||||
}, preview->lifetime());
|
}, preview->lifetime());
|
||||||
|
|
||||||
preview->peerRemoved(
|
preview->peerRemoved(
|
||||||
) | rpl::start_with_next([=](not_null<History*> history) {
|
) | rpl::start_with_next([=](not_null<History*> history) {
|
||||||
auto always = data->always();
|
auto always = data->always();
|
||||||
|
auto pinned = data->pinned();
|
||||||
auto never = data->never();
|
auto never = data->never();
|
||||||
always.remove(history);
|
always.remove(history);
|
||||||
|
pinned.erase(ranges::remove(pinned, history), end(pinned));
|
||||||
never.remove(history);
|
never.remove(history);
|
||||||
*data = Data::ChatFilter(
|
*data = Data::ChatFilter(
|
||||||
data->id(),
|
data->id(),
|
||||||
data->title(),
|
data->title(),
|
||||||
data->flags(),
|
data->flags(),
|
||||||
std::move(always),
|
std::move(always),
|
||||||
|
std::move(pinned),
|
||||||
std::move(never));
|
std::move(never));
|
||||||
}, preview->lifetime());
|
}, preview->lifetime());
|
||||||
|
|
||||||
|
@ -272,7 +276,7 @@ void EditExceptions(
|
||||||
const auto peers = box->peerListCollectSelectedRows();
|
const auto peers = box->peerListCollectSelectedRows();
|
||||||
auto &&histories = ranges::view::all(
|
auto &&histories = ranges::view::all(
|
||||||
peers
|
peers
|
||||||
) | ranges::view::transform([=](not_null<PeerData*> peer) {
|
) | ranges::view::transform([=](not_null<PeerData*> peer) {
|
||||||
return window->session().data().history(peer);
|
return window->session().data().history(peer);
|
||||||
});
|
});
|
||||||
auto changed = base::flat_set<not_null<History*>>{
|
auto changed = base::flat_set<not_null<History*>>{
|
||||||
|
@ -283,11 +287,17 @@ void EditExceptions(
|
||||||
for (const auto &history : changed) {
|
for (const auto &history : changed) {
|
||||||
removeFrom.remove(history);
|
removeFrom.remove(history);
|
||||||
}
|
}
|
||||||
|
auto pinned = data->pinned();
|
||||||
|
pinned.erase(ranges::remove_if(pinned, [&](not_null<History*> history) {
|
||||||
|
const auto contains = changed.contains(history);
|
||||||
|
return include ? !contains : contains;
|
||||||
|
}), end(pinned));
|
||||||
*data = Data::ChatFilter(
|
*data = Data::ChatFilter(
|
||||||
data->id(),
|
data->id(),
|
||||||
data->title(),
|
data->title(),
|
||||||
(data->flags() & ~options) | rawController->chosenOptions(),
|
(data->flags() & ~options) | rawController->chosenOptions(),
|
||||||
include ? std::move(changed) : std::move(removeFrom),
|
include ? std::move(changed) : std::move(removeFrom),
|
||||||
|
std::move(pinned),
|
||||||
include ? std::move(removeFrom) : std::move(changed));
|
include ? std::move(removeFrom) : std::move(changed));
|
||||||
refresh();
|
refresh();
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
|
@ -407,6 +417,7 @@ void EditFilterBox(
|
||||||
title,
|
title,
|
||||||
data->flags(),
|
data->flags(),
|
||||||
data->always(),
|
data->always(),
|
||||||
|
data->pinned(),
|
||||||
data->never());
|
data->never());
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
|
|
||||||
|
|
|
@ -380,7 +380,10 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() {
|
||||||
if (_selected & flag) {
|
if (_selected & flag) {
|
||||||
if (const auto row = delegate->peerListFindRow(TypeId(flag))) {
|
if (const auto row = delegate->peerListFindRow(TypeId(flag))) {
|
||||||
content->changeCheckState(row, true, anim::type::instant);
|
content->changeCheckState(row, true, anim::type::instant);
|
||||||
this->delegate()->peerListSetForeignRowChecked(row, true);
|
this->delegate()->peerListSetForeignRowChecked(
|
||||||
|
row,
|
||||||
|
true,
|
||||||
|
anim::type::instant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,7 +400,8 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() {
|
||||||
) | rpl::start_with_next([=](RowSelectionChange update) {
|
) | rpl::start_with_next([=](RowSelectionChange update) {
|
||||||
this->delegate()->peerListSetForeignRowChecked(
|
this->delegate()->peerListSetForeignRowChecked(
|
||||||
update.row,
|
update.row,
|
||||||
update.checked);
|
update.checked,
|
||||||
|
anim::type::normal);
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
_deselectOption = [=](PeerListRowId itemId) {
|
_deselectOption = [=](PeerListRowId itemId) {
|
||||||
|
|
|
@ -203,9 +203,10 @@ void PeerListBox::peerListSetRowChecked(
|
||||||
|
|
||||||
void PeerListBox::peerListSetForeignRowChecked(
|
void PeerListBox::peerListSetForeignRowChecked(
|
||||||
not_null<PeerListRow*> row,
|
not_null<PeerListRow*> row,
|
||||||
bool checked) {
|
bool checked,
|
||||||
|
anim::type animated) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
addSelectItem(row, anim::type::normal);
|
addSelectItem(row, animated);
|
||||||
|
|
||||||
// This call deletes row from _searchRows.
|
// This call deletes row from _searchRows.
|
||||||
_select->entity()->clearQuery();
|
_select->entity()->clearQuery();
|
||||||
|
|
|
@ -271,7 +271,10 @@ public:
|
||||||
virtual void peerListConvertRowToSearchResult(not_null<PeerListRow*> row) = 0;
|
virtual void peerListConvertRowToSearchResult(not_null<PeerListRow*> row) = 0;
|
||||||
virtual bool peerListIsRowChecked(not_null<PeerListRow*> row) = 0;
|
virtual bool peerListIsRowChecked(not_null<PeerListRow*> row) = 0;
|
||||||
virtual void peerListSetRowChecked(not_null<PeerListRow*> row, bool checked) = 0;
|
virtual void peerListSetRowChecked(not_null<PeerListRow*> row, bool checked) = 0;
|
||||||
virtual void peerListSetForeignRowChecked(not_null<PeerListRow*> row, bool checked) = 0;
|
virtual void peerListSetForeignRowChecked(
|
||||||
|
not_null<PeerListRow*> row,
|
||||||
|
bool checked,
|
||||||
|
anim::type animated) = 0;
|
||||||
virtual not_null<PeerListRow*> peerListRowAt(int index) = 0;
|
virtual not_null<PeerListRow*> peerListRowAt(int index) = 0;
|
||||||
virtual void peerListRefreshRows() = 0;
|
virtual void peerListRefreshRows() = 0;
|
||||||
virtual void peerListScrollToTop() = 0;
|
virtual void peerListScrollToTop() = 0;
|
||||||
|
@ -724,7 +727,8 @@ public:
|
||||||
}
|
}
|
||||||
void peerListSetForeignRowChecked(
|
void peerListSetForeignRowChecked(
|
||||||
not_null<PeerListRow*> row,
|
not_null<PeerListRow*> row,
|
||||||
bool checked) override {
|
bool checked,
|
||||||
|
anim::type animated) override {
|
||||||
}
|
}
|
||||||
int peerListFullRowsCount() override {
|
int peerListFullRowsCount() override {
|
||||||
return _content->fullRowsCount();
|
return _content->fullRowsCount();
|
||||||
|
@ -816,7 +820,8 @@ public:
|
||||||
bool checked) override;
|
bool checked) override;
|
||||||
void peerListSetForeignRowChecked(
|
void peerListSetForeignRowChecked(
|
||||||
not_null<PeerListRow*> row,
|
not_null<PeerListRow*> row,
|
||||||
bool checked) override;
|
bool checked,
|
||||||
|
anim::type animated) override;
|
||||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||||
int peerListSelectedRowsCount() override;
|
int peerListSelectedRowsCount() override;
|
||||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||||
|
|
|
@ -25,10 +25,12 @@ ChatFilter::ChatFilter(
|
||||||
const QString &title,
|
const QString &title,
|
||||||
Flags flags,
|
Flags flags,
|
||||||
base::flat_set<not_null<History*>> always,
|
base::flat_set<not_null<History*>> always,
|
||||||
|
std::vector<not_null<History*>> pinned,
|
||||||
base::flat_set<not_null<History*>> never)
|
base::flat_set<not_null<History*>> never)
|
||||||
: _id(id)
|
: _id(id)
|
||||||
, _title(title)
|
, _title(title)
|
||||||
, _always(std::move(always))
|
, _always(std::move(always))
|
||||||
|
, _pinned(std::move(pinned))
|
||||||
, _never(std::move(never))
|
, _never(std::move(never))
|
||||||
, _flags(flags) {
|
, _flags(flags) {
|
||||||
}
|
}
|
||||||
|
@ -66,17 +68,26 @@ ChatFilter ChatFilter::FromTL(
|
||||||
}) | ranges::view::transform([](History *history) {
|
}) | ranges::view::transform([](History *history) {
|
||||||
return not_null<History*>(history);
|
return not_null<History*>(history);
|
||||||
});
|
});
|
||||||
auto &&always = ranges::view::all(
|
auto &&always = ranges::view::concat(
|
||||||
data.vinclude_peers().v
|
data.vinclude_peers().v
|
||||||
) | to_histories;
|
) | to_histories;
|
||||||
|
auto pinned = ranges::view::all(
|
||||||
|
data.vpinned_peers().v
|
||||||
|
) | to_histories | ranges::to_vector;
|
||||||
auto &&never = ranges::view::all(
|
auto &&never = ranges::view::all(
|
||||||
data.vexclude_peers().v
|
data.vexclude_peers().v
|
||||||
) | to_histories;
|
) | to_histories;
|
||||||
|
auto &&all = ranges::view::concat(always, pinned);
|
||||||
|
auto list = base::flat_set<not_null<History*>>{
|
||||||
|
all.begin(),
|
||||||
|
all.end()
|
||||||
|
};
|
||||||
return ChatFilter(
|
return ChatFilter(
|
||||||
data.vid().v,
|
data.vid().v,
|
||||||
qs(data.vtitle()),
|
qs(data.vtitle()),
|
||||||
flags,
|
flags,
|
||||||
{ always.begin(), always.end() },
|
std::move(list),
|
||||||
|
std::move(pinned),
|
||||||
{ never.begin(), never.end() });
|
{ never.begin(), never.end() });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -94,10 +105,17 @@ MTPDialogFilter ChatFilter::tl() const {
|
||||||
| ((_flags & Flag::NoArchived)
|
| ((_flags & Flag::NoArchived)
|
||||||
? TLFlag::f_exclude_archived
|
? TLFlag::f_exclude_archived
|
||||||
: TLFlag(0));
|
: TLFlag(0));
|
||||||
auto always = QVector<MTPInputPeer>();
|
auto always = _always;
|
||||||
always.reserve(_always.size());
|
auto pinned = QVector<MTPInputPeer>();
|
||||||
for (const auto history : _always) {
|
pinned.reserve(_pinned.size());
|
||||||
always.push_back(history->peer->input);
|
for (const auto history : _pinned) {
|
||||||
|
pinned.push_back(history->peer->input);
|
||||||
|
always.remove(history);
|
||||||
|
}
|
||||||
|
auto include = QVector<MTPInputPeer>();
|
||||||
|
include.reserve(always.size());
|
||||||
|
for (const auto history : always) {
|
||||||
|
include.push_back(history->peer->input);
|
||||||
}
|
}
|
||||||
auto never = QVector<MTPInputPeer>();
|
auto never = QVector<MTPInputPeer>();
|
||||||
never.reserve(_never.size());
|
never.reserve(_never.size());
|
||||||
|
@ -109,8 +127,8 @@ MTPDialogFilter ChatFilter::tl() const {
|
||||||
MTP_int(_id),
|
MTP_int(_id),
|
||||||
MTP_string(_title),
|
MTP_string(_title),
|
||||||
MTPstring(), // emoticon
|
MTPstring(), // emoticon
|
||||||
MTP_vector<MTPInputPeer>(),
|
MTP_vector<MTPInputPeer>(pinned),
|
||||||
MTP_vector<MTPInputPeer>(always),
|
MTP_vector<MTPInputPeer>(include),
|
||||||
MTP_vector<MTPInputPeer>(never));
|
MTP_vector<MTPInputPeer>(never));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +148,10 @@ const base::flat_set<not_null<History*>> &ChatFilter::always() const {
|
||||||
return _always;
|
return _always;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<not_null<History*>> &ChatFilter::pinned() const {
|
||||||
|
return _pinned;
|
||||||
|
}
|
||||||
|
|
||||||
const base::flat_set<not_null<History*>> &ChatFilter::never() const {
|
const base::flat_set<not_null<History*>> &ChatFilter::never() const {
|
||||||
return _never;
|
return _never;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +218,7 @@ not_null<Dialogs::MainList*> ChatFilters::chatsList(FilterId filterId) {
|
||||||
if (!pointer) {
|
if (!pointer) {
|
||||||
pointer = std::make_unique<Dialogs::MainList>(
|
pointer = std::make_unique<Dialogs::MainList>(
|
||||||
filterId,
|
filterId,
|
||||||
rpl::single(1));
|
rpl::single(ChatFilter::kPinnedLimit));
|
||||||
}
|
}
|
||||||
return pointer.get();
|
return pointer.get();
|
||||||
}
|
}
|
||||||
|
@ -284,7 +306,7 @@ void ChatFilters::applyInsert(ChatFilter filter, int position) {
|
||||||
|
|
||||||
_list.insert(
|
_list.insert(
|
||||||
begin(_list) + position,
|
begin(_list) + position,
|
||||||
ChatFilter(filter.id(), {}, {}, {}, {}));
|
ChatFilter(filter.id(), {}, {}, {}, {}, {}));
|
||||||
applyChange(*(begin(_list) + position), std::move(filter));
|
applyChange(*(begin(_list) + position), std::move(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +323,7 @@ void ChatFilters::applyRemove(int position) {
|
||||||
Expects(position >= 0 && position < _list.size());
|
Expects(position >= 0 && position < _list.size());
|
||||||
|
|
||||||
const auto i = begin(_list) + position;
|
const auto i = begin(_list) + position;
|
||||||
applyChange(*i, ChatFilter(i->id(), {}, {}, {}, {}));
|
applyChange(*i, ChatFilter(i->id(), {}, {}, {}, {}, {}));
|
||||||
_list.erase(i);
|
_list.erase(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,12 +331,21 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) {
|
||||||
const auto rulesChanged = (filter.flags() != updated.flags())
|
const auto rulesChanged = (filter.flags() != updated.flags())
|
||||||
|| (filter.always() != updated.always())
|
|| (filter.always() != updated.always())
|
||||||
|| (filter.never() != updated.never());
|
|| (filter.never() != updated.never());
|
||||||
|
const auto pinnedChanged = (filter.pinned() != updated.pinned());
|
||||||
|
if (!rulesChanged
|
||||||
|
&& !pinnedChanged
|
||||||
|
&& filter.title() == updated.title()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (rulesChanged) {
|
if (rulesChanged) {
|
||||||
const auto id = filter.id();
|
const auto id = filter.id();
|
||||||
const auto filterList = _owner->chatsFilters().chatsList(id);
|
const auto filterList = _owner->chatsFilters().chatsList(id);
|
||||||
const auto feedHistory = [&](not_null<History*> history) {
|
const auto feedHistory = [&](not_null<History*> history) {
|
||||||
const auto now = updated.contains(history);
|
const auto now = updated.contains(history);
|
||||||
const auto was = filter.contains(history);
|
const auto was = filter.contains(history);
|
||||||
|
if (now) {
|
||||||
|
history->applyFilterPinnedIndex(id, updated);
|
||||||
|
}
|
||||||
if (now != was) {
|
if (now != was) {
|
||||||
if (now) {
|
if (now) {
|
||||||
history->addToChatList(id, filterList);
|
history->addToChatList(id, filterList);
|
||||||
|
@ -334,8 +365,10 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) {
|
||||||
if (const auto folder = _owner->folderLoaded(Data::Folder::kId)) {
|
if (const auto folder = _owner->folderLoaded(Data::Folder::kId)) {
|
||||||
feedList(folder->chatsList());
|
feedList(folder->chatsList());
|
||||||
}
|
}
|
||||||
} else if (filter.title() == updated.title()) {
|
} else if (pinnedChanged) {
|
||||||
return false;
|
const auto id = filter.id();
|
||||||
|
const auto filterList = _owner->chatsFilters().chatsList(id);
|
||||||
|
filterList->pinned()->applyList(updated.pinned());
|
||||||
}
|
}
|
||||||
filter = std::move(updated);
|
filter = std::move(updated);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -34,12 +34,15 @@ public:
|
||||||
friend constexpr inline bool is_flag_type(Flag) { return true; };
|
friend constexpr inline bool is_flag_type(Flag) { return true; };
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
|
|
||||||
|
static constexpr int kPinnedLimit = 100;
|
||||||
|
|
||||||
ChatFilter() = default;
|
ChatFilter() = default;
|
||||||
ChatFilter(
|
ChatFilter(
|
||||||
FilterId id,
|
FilterId id,
|
||||||
const QString &title,
|
const QString &title,
|
||||||
Flags flags,
|
Flags flags,
|
||||||
base::flat_set<not_null<History*>> always,
|
base::flat_set<not_null<History*>> always,
|
||||||
|
std::vector<not_null<History*>> pinned,
|
||||||
base::flat_set<not_null<History*>> never);
|
base::flat_set<not_null<History*>> never);
|
||||||
|
|
||||||
[[nodiscard]] static ChatFilter FromTL(
|
[[nodiscard]] static ChatFilter FromTL(
|
||||||
|
@ -51,6 +54,7 @@ public:
|
||||||
[[nodiscard]] QString title() const;
|
[[nodiscard]] QString title() const;
|
||||||
[[nodiscard]] Flags flags() const;
|
[[nodiscard]] Flags flags() const;
|
||||||
[[nodiscard]] const base::flat_set<not_null<History*>> &always() const;
|
[[nodiscard]] const base::flat_set<not_null<History*>> &always() const;
|
||||||
|
[[nodiscard]] const std::vector<not_null<History*>> &pinned() const;
|
||||||
[[nodiscard]] const base::flat_set<not_null<History*>> &never() const;
|
[[nodiscard]] const base::flat_set<not_null<History*>> &never() const;
|
||||||
|
|
||||||
[[nodiscard]] bool contains(not_null<History*> history) const;
|
[[nodiscard]] bool contains(not_null<History*> history) const;
|
||||||
|
@ -59,6 +63,7 @@ private:
|
||||||
FilterId _id = 0;
|
FilterId _id = 0;
|
||||||
QString _title;
|
QString _title;
|
||||||
base::flat_set<not_null<History*>> _always;
|
base::flat_set<not_null<History*>> _always;
|
||||||
|
std::vector<not_null<History*>> _pinned;
|
||||||
base::flat_set<not_null<History*>> _never;
|
base::flat_set<not_null<History*>> _never;
|
||||||
Flags _flags;
|
Flags _flags;
|
||||||
|
|
||||||
|
|
|
@ -347,7 +347,7 @@ void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
|
||||||
if (folderId != 0) {
|
if (folderId != 0) {
|
||||||
LOG(("API Error: Nested folders detected."));
|
LOG(("API Error: Nested folders detected."));
|
||||||
}
|
}
|
||||||
owner().setChatPinned(this, data.is_pinned());
|
owner().setChatPinned(this, FilterId(), data.is_pinned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// #feed
|
// #feed
|
||||||
|
|
|
@ -780,7 +780,7 @@ void Session::deleteConversationLocally(not_null<PeerData*> peer) {
|
||||||
const auto history = historyLoaded(peer);
|
const auto history = historyLoaded(peer);
|
||||||
if (history) {
|
if (history) {
|
||||||
if (history->folderKnown()) {
|
if (history->folderKnown()) {
|
||||||
setChatPinned(history, false);
|
setChatPinned(history, FilterId(), false);
|
||||||
}
|
}
|
||||||
App::main()->removeDialog(history);
|
App::main()->removeDialog(history);
|
||||||
history->clear(peer->isChannel()
|
history->clear(peer->isChannel()
|
||||||
|
@ -1459,11 +1459,16 @@ MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const {
|
||||||
return { 1, item->fullId() };
|
return { 1, item->fullId() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setChatPinned(const Dialogs::Key &key, bool pinned) {
|
void Session::setChatPinned(
|
||||||
|
const Dialogs::Key &key,
|
||||||
|
FilterId filterId,
|
||||||
|
bool pinned) {
|
||||||
Expects(key.entry()->folderKnown());
|
Expects(key.entry()->folderKnown());
|
||||||
|
|
||||||
const auto list = chatsList(key.entry()->folder())->pinned();
|
const auto list = filterId
|
||||||
list->setPinned(key, pinned);
|
? chatsFilters().chatsList(filterId)
|
||||||
|
: chatsList(key.entry()->folder());
|
||||||
|
list->pinned()->setPinned(key, pinned);
|
||||||
notifyPinnedDialogsOrderUpdated();
|
notifyPinnedDialogsOrderUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1549,32 +1554,53 @@ void Session::applyDialog(
|
||||||
setPinnedFromDialog(folder, data.is_pinned());
|
setPinnedFromDialog(folder, data.is_pinned());
|
||||||
}
|
}
|
||||||
|
|
||||||
int Session::pinnedChatsCount(Data::Folder *folder) const {
|
int Session::pinnedChatsCount(
|
||||||
return pinnedChatsOrder(folder).size();
|
Data::Folder *folder,
|
||||||
|
FilterId filterId) const {
|
||||||
|
Expects(!folder || !filterId);
|
||||||
|
|
||||||
|
if (!filterId) {
|
||||||
|
return pinnedChatsOrder(folder, filterId).size();
|
||||||
|
}
|
||||||
|
const auto &list = chatsFilters().list();
|
||||||
|
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
|
||||||
|
return (i != end(list)) ? i->pinned().size() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Session::pinnedChatsLimit(Data::Folder *folder) const {
|
int Session::pinnedChatsLimit(
|
||||||
return folder
|
Data::Folder *folder,
|
||||||
|
FilterId filterId) const {
|
||||||
|
return filterId
|
||||||
|
? Data::ChatFilter::kPinnedLimit
|
||||||
|
: folder
|
||||||
? Global::PinnedDialogsInFolderMax()
|
? Global::PinnedDialogsInFolderMax()
|
||||||
: Global::PinnedDialogsCountMax();
|
: Global::PinnedDialogsCountMax();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
|
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
|
||||||
Data::Folder *folder) const {
|
Data::Folder *folder,
|
||||||
return chatsList(folder)->pinned()->order();
|
FilterId filterId) const {
|
||||||
|
const auto list = filterId
|
||||||
|
? chatsFilters().chatsList(filterId)
|
||||||
|
: chatsList(folder);
|
||||||
|
return list->pinned()->order();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::clearPinnedChats(Data::Folder *folder) {
|
void Session::clearPinnedChats(Data::Folder *folder, FilterId filterId) {
|
||||||
chatsList(folder)->pinned()->clear();
|
chatsList(folder)->pinned()->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::reorderTwoPinnedChats(
|
void Session::reorderTwoPinnedChats(
|
||||||
|
FilterId filterId,
|
||||||
const Dialogs::Key &key1,
|
const Dialogs::Key &key1,
|
||||||
const Dialogs::Key &key2) {
|
const Dialogs::Key &key2) {
|
||||||
Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown());
|
Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown());
|
||||||
Expects(key1.entry()->folder() == key2.entry()->folder());
|
Expects(filterId || (key1.entry()->folder() == key2.entry()->folder()));
|
||||||
|
|
||||||
chatsList(key1.entry()->folder())->pinned()->reorder(key1, key2);
|
const auto list = filterId
|
||||||
|
? chatsFilters().chatsList(filterId)
|
||||||
|
: chatsList(key1.entry()->folder());
|
||||||
|
list->pinned()->reorder(key1, key2);
|
||||||
notifyPinnedDialogsOrderUpdated();
|
notifyPinnedDialogsOrderUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3349,6 +3375,7 @@ auto Session::refreshChatListEntry(
|
||||||
const auto filterList = chatsFilters().chatsList(id);
|
const auto filterList = chatsFilters().chatsList(id);
|
||||||
auto filterResult = RefreshChatListEntryResult();
|
auto filterResult = RefreshChatListEntryResult();
|
||||||
if (filter.contains(history)) {
|
if (filter.contains(history)) {
|
||||||
|
history->applyFilterPinnedIndex(id, filter);
|
||||||
filterResult.changed = !entry->inChatList(id);
|
filterResult.changed = !entry->inChatList(id);
|
||||||
if (filterResult.changed) {
|
if (filterResult.changed) {
|
||||||
entry->addToChatList(id, filterList);
|
entry->addToChatList(id, filterList);
|
||||||
|
|
|
@ -362,16 +362,21 @@ public:
|
||||||
const QVector<MTPDialog> &dialogs,
|
const QVector<MTPDialog> &dialogs,
|
||||||
std::optional<int> count = std::nullopt);
|
std::optional<int> count = std::nullopt);
|
||||||
|
|
||||||
int pinnedChatsCount(Data::Folder *folder) const;
|
int pinnedChatsCount(Data::Folder *folder, FilterId filterId) const;
|
||||||
int pinnedChatsLimit(Data::Folder *folder) const;
|
int pinnedChatsLimit(Data::Folder *folder, FilterId filterId) const;
|
||||||
const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||||
Data::Folder *folder) const;
|
Data::Folder *folder,
|
||||||
void setChatPinned(const Dialogs::Key &key, bool pinned);
|
FilterId filterId) const;
|
||||||
void clearPinnedChats(Data::Folder *folder);
|
void setChatPinned(
|
||||||
|
const Dialogs::Key &key,
|
||||||
|
FilterId filterId,
|
||||||
|
bool pinned);
|
||||||
|
void clearPinnedChats(Data::Folder *folder, FilterId filterId);
|
||||||
void applyPinnedChats(
|
void applyPinnedChats(
|
||||||
Data::Folder *folder,
|
Data::Folder *folder,
|
||||||
const QVector<MTPDialogPeer> &list);
|
const QVector<MTPDialogPeer> &list);
|
||||||
void reorderTwoPinnedChats(
|
void reorderTwoPinnedChats(
|
||||||
|
FilterId filterId,
|
||||||
const Dialogs::Key &key1,
|
const Dialogs::Key &key1,
|
||||||
const Dialogs::Key &key2);
|
const Dialogs::Key &key2);
|
||||||
|
|
||||||
|
|
|
@ -55,19 +55,34 @@ Main::Session &Entry::session() const {
|
||||||
return _owner->session();
|
return _owner->session();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Entry::cachePinnedIndex(int index) {
|
void Entry::pinnedIndexChanged(int was, int now) {
|
||||||
if (_pinnedIndex != index) {
|
if (session().supportMode()) {
|
||||||
const auto wasPinned = isPinnedDialog();
|
// Force reorder in support mode.
|
||||||
_pinnedIndex = index;
|
_sortKeyInChatList = 0;
|
||||||
if (session().supportMode()) {
|
}
|
||||||
// Force reorder in support mode.
|
updateChatListSortPosition();
|
||||||
_sortKeyInChatList = 0;
|
updateChatListEntry();
|
||||||
}
|
if ((was != 0) != (now != 0)) {
|
||||||
updateChatListSortPosition();
|
changedChatListPinHook();
|
||||||
updateChatListEntry();
|
}
|
||||||
if (wasPinned != isPinnedDialog()) {
|
}
|
||||||
changedChatListPinHook();
|
|
||||||
|
void Entry::cachePinnedIndex(FilterId filterId, int index) {
|
||||||
|
const auto i = _pinnedIndex.find(filterId);
|
||||||
|
const auto was = (i != end(_pinnedIndex)) ? i->second : 0;
|
||||||
|
if (index == was) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!index) {
|
||||||
|
_pinnedIndex.erase(i);
|
||||||
|
pinnedIndexChanged(was, index);
|
||||||
|
} else {
|
||||||
|
if (!was) {
|
||||||
|
_pinnedIndex.emplace(filterId, index);
|
||||||
|
} else {
|
||||||
|
i->second = index;
|
||||||
}
|
}
|
||||||
|
pinnedIndexChanged(was, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,9 +112,7 @@ void Entry::updateChatListSortPosition() {
|
||||||
const auto fixedIndex = fixedOnTopIndex();
|
const auto fixedIndex = fixedOnTopIndex();
|
||||||
_sortKeyInChatList = fixedIndex
|
_sortKeyInChatList = fixedIndex
|
||||||
? FixedOnTopDialogPos(fixedIndex)
|
? FixedOnTopDialogPos(fixedIndex)
|
||||||
: isPinnedDialog()
|
: computeSortPosition(0);
|
||||||
? PinnedDialogPos(_pinnedIndex)
|
|
||||||
: _sortKeyByDate;
|
|
||||||
if (needUpdateInChatList()) {
|
if (needUpdateInChatList()) {
|
||||||
setChatListExistence(true);
|
setChatListExistence(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,6 +120,23 @@ void Entry::updateChatListSortPosition() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Entry::lookupPinnedIndex(FilterId filterId) const {
|
||||||
|
if (filterId) {
|
||||||
|
const auto i = _pinnedIndex.find(filterId);
|
||||||
|
return (i != end(_pinnedIndex)) ? i->second : 0;
|
||||||
|
} else if (!_pinnedIndex.empty()) {
|
||||||
|
return _pinnedIndex.front().first
|
||||||
|
? 0
|
||||||
|
: _pinnedIndex.front().second;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 Entry::computeSortPosition(FilterId filterId) const {
|
||||||
|
const auto index = lookupPinnedIndex(filterId);
|
||||||
|
return index ? PinnedDialogPos(index) : _sortKeyByDate;
|
||||||
|
}
|
||||||
|
|
||||||
void Entry::updateChatListExistence() {
|
void Entry::updateChatListExistence() {
|
||||||
setChatListExistence(shouldBeInChatList());
|
setChatListExistence(shouldBeInChatList());
|
||||||
}
|
}
|
||||||
|
@ -205,6 +235,9 @@ void Entry::removeFromChatList(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_chatListLinks.erase(i);
|
_chatListLinks.erase(i);
|
||||||
|
if (isPinnedDialog(filterId)) {
|
||||||
|
owner().setChatPinned(_key, filterId, false);
|
||||||
|
}
|
||||||
list->removeEntry(_key);
|
list->removeEntry(_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,9 @@ struct RowsByLetter {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SortMode {
|
enum class SortMode {
|
||||||
Complex = 0x00,
|
Date = 0x00,
|
||||||
Date = 0x01,
|
Name = 0x01,
|
||||||
Name = 0x02,
|
Add = 0x02,
|
||||||
Add = 0x04,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PositionChange {
|
struct PositionChange {
|
||||||
|
@ -117,19 +116,18 @@ public:
|
||||||
QChar letter,
|
QChar letter,
|
||||||
not_null<Row*> row);
|
not_null<Row*> row);
|
||||||
void updateChatListEntry() const;
|
void updateChatListEntry() const;
|
||||||
bool isPinnedDialog() const {
|
[[nodiscard]] bool isPinnedDialog(FilterId filterId) const {
|
||||||
return _pinnedIndex > 0;
|
return lookupPinnedIndex(filterId) != 0;
|
||||||
}
|
}
|
||||||
void cachePinnedIndex(int index);
|
void cachePinnedIndex(FilterId filterId, int index);
|
||||||
bool isProxyPromoted() const {
|
bool isProxyPromoted() const {
|
||||||
return _isProxyPromoted;
|
return _isProxyPromoted;
|
||||||
}
|
}
|
||||||
void cacheProxyPromoted(bool promoted);
|
void cacheProxyPromoted(bool promoted);
|
||||||
uint64 sortKeyInChatList() const {
|
[[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const {
|
||||||
return _sortKeyInChatList;
|
return filterId
|
||||||
}
|
? computeSortPosition(filterId)
|
||||||
uint64 sortKeyByDate() const {
|
: _sortKeyInChatList;
|
||||||
return _sortKeyByDate;
|
|
||||||
}
|
}
|
||||||
void updateChatListSortPosition();
|
void updateChatListSortPosition();
|
||||||
void setChatListTimeId(TimeId date);
|
void setChatListTimeId(TimeId date);
|
||||||
|
@ -194,8 +192,12 @@ protected:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int lookupPinnedIndex(FilterId filterId) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void changedChatListPinHook();
|
virtual void changedChatListPinHook();
|
||||||
|
void pinnedIndexChanged(int was, int now);
|
||||||
|
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
|
||||||
|
|
||||||
void setChatListExistence(bool exists);
|
void setChatListExistence(bool exists);
|
||||||
RowsByLetter *chatListLinks(FilterId filterId);
|
RowsByLetter *chatListLinks(FilterId filterId);
|
||||||
|
@ -208,7 +210,7 @@ private:
|
||||||
base::flat_map<FilterId, RowsByLetter> _chatListLinks;
|
base::flat_map<FilterId, RowsByLetter> _chatListLinks;
|
||||||
uint64 _sortKeyInChatList = 0;
|
uint64 _sortKeyInChatList = 0;
|
||||||
uint64 _sortKeyByDate = 0;
|
uint64 _sortKeyByDate = 0;
|
||||||
int _pinnedIndex = 0;
|
base::flat_map<FilterId, int> _pinnedIndex;
|
||||||
bool _isProxyPromoted = false;
|
bool _isProxyPromoted = false;
|
||||||
TimeId _timeId = 0;
|
TimeId _timeId = 0;
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
|
||||||
IndexedList::IndexedList(SortMode sortMode)
|
IndexedList::IndexedList(SortMode sortMode, FilterId filterId)
|
||||||
: _sortMode(sortMode)
|
: _sortMode(sortMode)
|
||||||
, _list(sortMode)
|
, _filterId(filterId)
|
||||||
, _empty(sortMode) {
|
, _list(sortMode, filterId)
|
||||||
|
, _empty(sortMode, filterId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
RowsByLetter IndexedList::addToEnd(Key key) {
|
RowsByLetter IndexedList::addToEnd(Key key) {
|
||||||
|
@ -28,7 +29,7 @@ RowsByLetter IndexedList::addToEnd(Key key) {
|
||||||
for (const auto ch : key.entry()->chatListFirstLetters()) {
|
for (const auto ch : key.entry()->chatListFirstLetters()) {
|
||||||
auto j = _index.find(ch);
|
auto j = _index.find(ch);
|
||||||
if (j == _index.cend()) {
|
if (j == _index.cend()) {
|
||||||
j = _index.emplace(ch, _sortMode).first;
|
j = _index.emplace(ch, _sortMode, _filterId).first;
|
||||||
}
|
}
|
||||||
result.letters.emplace(ch, j->second.addToEnd(key));
|
result.letters.emplace(ch, j->second.addToEnd(key));
|
||||||
}
|
}
|
||||||
|
@ -44,7 +45,7 @@ Row *IndexedList::addByName(Key key) {
|
||||||
for (const auto ch : key.entry()->chatListFirstLetters()) {
|
for (const auto ch : key.entry()->chatListFirstLetters()) {
|
||||||
auto j = _index.find(ch);
|
auto j = _index.find(ch);
|
||||||
if (j == _index.cend()) {
|
if (j == _index.cend()) {
|
||||||
j = _index.emplace(ch, _sortMode).first;
|
j = _index.emplace(ch, _sortMode, _filterId).first;
|
||||||
}
|
}
|
||||||
j->second.addByName(key);
|
j->second.addByName(key);
|
||||||
}
|
}
|
||||||
|
@ -79,7 +80,8 @@ void IndexedList::movePinned(Row *row, int deltaSign) {
|
||||||
Assert(swapPinnedIndexWith != cbegin());
|
Assert(swapPinnedIndexWith != cbegin());
|
||||||
--swapPinnedIndexWith;
|
--swapPinnedIndexWith;
|
||||||
}
|
}
|
||||||
Auth().data().reorderTwoPinnedChats(
|
row->key().entry()->owner().reorderTwoPinnedChats(
|
||||||
|
_filterId,
|
||||||
row->key(),
|
row->key(),
|
||||||
(*swapPinnedIndexWith)->key());
|
(*swapPinnedIndexWith)->key());
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,7 @@ void IndexedList::movePinned(Row *row, int deltaSign) {
|
||||||
void IndexedList::peerNameChanged(
|
void IndexedList::peerNameChanged(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const base::flat_set<QChar> &oldLetters) {
|
const base::flat_set<QChar> &oldLetters) {
|
||||||
Expects(_sortMode != SortMode::Date && _sortMode != SortMode::Complex);
|
Expects(_sortMode != SortMode::Date);
|
||||||
|
|
||||||
if (const auto history = peer->owner().historyLoaded(peer)) {
|
if (const auto history = peer->owner().historyLoaded(peer)) {
|
||||||
if (_sortMode == SortMode::Name) {
|
if (_sortMode == SortMode::Name) {
|
||||||
|
@ -102,7 +104,7 @@ void IndexedList::peerNameChanged(
|
||||||
FilterId filterId,
|
FilterId filterId,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const base::flat_set<QChar> &oldLetters) {
|
const base::flat_set<QChar> &oldLetters) {
|
||||||
Expects(_sortMode == SortMode::Date || _sortMode == SortMode::Complex);
|
Expects(_sortMode == SortMode::Date);
|
||||||
|
|
||||||
if (const auto history = peer->owner().historyLoaded(peer)) {
|
if (const auto history = peer->owner().historyLoaded(peer)) {
|
||||||
adjustNames(filterId, history, oldLetters);
|
adjustNames(filterId, history, oldLetters);
|
||||||
|
@ -139,7 +141,7 @@ void IndexedList::adjustByName(
|
||||||
for (auto ch : toAdd) {
|
for (auto ch : toAdd) {
|
||||||
auto j = _index.find(ch);
|
auto j = _index.find(ch);
|
||||||
if (j == _index.cend()) {
|
if (j == _index.cend()) {
|
||||||
j = _index.emplace(ch, _sortMode).first;
|
j = _index.emplace(ch, _sortMode, _filterId).first;
|
||||||
}
|
}
|
||||||
j->second.addByName(key);
|
j->second.addByName(key);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +167,7 @@ void IndexedList::adjustNames(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto ch : toRemove) {
|
for (auto ch : toRemove) {
|
||||||
if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) {
|
if (_sortMode == SortMode::Date) {
|
||||||
history->removeChatListEntryByLetter(filterId, ch);
|
history->removeChatListEntryByLetter(filterId, ch);
|
||||||
}
|
}
|
||||||
if (auto it = _index.find(ch); it != _index.cend()) {
|
if (auto it = _index.find(ch); it != _index.cend()) {
|
||||||
|
@ -175,10 +177,10 @@ void IndexedList::adjustNames(
|
||||||
for (auto ch : toAdd) {
|
for (auto ch : toAdd) {
|
||||||
auto j = _index.find(ch);
|
auto j = _index.find(ch);
|
||||||
if (j == _index.cend()) {
|
if (j == _index.cend()) {
|
||||||
j = _index.emplace(ch, _sortMode).first;
|
j = _index.emplace(ch, _sortMode, _filterId).first;
|
||||||
}
|
}
|
||||||
auto row = j->second.addToEnd(key);
|
auto row = j->second.addToEnd(key);
|
||||||
if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) {
|
if (_sortMode == SortMode::Date) {
|
||||||
history->addChatListEntryByLetter(filterId, ch, row);
|
history->addChatListEntryByLetter(filterId, ch, row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace Dialogs {
|
||||||
|
|
||||||
class IndexedList {
|
class IndexedList {
|
||||||
public:
|
public:
|
||||||
IndexedList(SortMode sortMode);
|
IndexedList(SortMode sortMode, FilterId filterId = 0);
|
||||||
|
|
||||||
RowsByLetter addToEnd(Key key);
|
RowsByLetter addToEnd(Key key);
|
||||||
Row *addByName(Key key);
|
Row *addByName(Key key);
|
||||||
|
@ -81,6 +81,7 @@ private:
|
||||||
const base::flat_set<QChar> &oldChars);
|
const base::flat_set<QChar> &oldChars);
|
||||||
|
|
||||||
SortMode _sortMode = SortMode();
|
SortMode _sortMode = SortMode();
|
||||||
|
FilterId _filterId = 0;
|
||||||
List _list, _empty;
|
List _list, _empty;
|
||||||
base::flat_map<QChar, List> _index;
|
base::flat_map<QChar, List> _index;
|
||||||
|
|
||||||
|
|
|
@ -66,12 +66,14 @@ int FixedOnTopDialogsCount(not_null<Dialogs::IndexedList*> list) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PinnedDialogsCount(not_null<Dialogs::IndexedList*> list) {
|
int PinnedDialogsCount(
|
||||||
|
FilterId filterId,
|
||||||
|
not_null<Dialogs::IndexedList*> list) {
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
for (const auto row : *list) {
|
for (const auto row : *list) {
|
||||||
if (row->entry()->fixedOnTopIndex()) {
|
if (row->entry()->fixedOnTopIndex()) {
|
||||||
continue;
|
continue;
|
||||||
} else if (!row->entry()->isPinnedDialog()) {
|
} else if (!row->entry()->isPinnedDialog(filterId)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++result;
|
++result;
|
||||||
|
@ -1075,7 +1077,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (anim::Disabled()
|
if (anim::Disabled()
|
||||||
&& (!_pressed || !_pressed->entry()->isPinnedDialog())) {
|
&& (!_pressed || !_pressed->entry()->isPinnedDialog(_filterId))) {
|
||||||
mousePressReleased(e->globalPos(), e->button());
|
mousePressReleased(e->globalPos(), e->button());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1091,7 +1093,9 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
|
||||||
if (updateReorderIndexGetCount() < 2) {
|
if (updateReorderIndexGetCount() < 2) {
|
||||||
_dragging = nullptr;
|
_dragging = nullptr;
|
||||||
} else {
|
} else {
|
||||||
const auto &order = session().data().pinnedChatsOrder(_openedFolder);
|
const auto &order = session().data().pinnedChatsOrder(
|
||||||
|
_openedFolder,
|
||||||
|
_filterId);
|
||||||
_pinnedOnDragStart = base::flat_set<Key>{
|
_pinnedOnDragStart = base::flat_set<Key>{
|
||||||
order.begin(),
|
order.begin(),
|
||||||
order.end()
|
order.end()
|
||||||
|
@ -1103,14 +1107,14 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int InnerWidget::countPinnedIndex(Row *ofRow) {
|
int InnerWidget::countPinnedIndex(Row *ofRow) {
|
||||||
if (!ofRow || !ofRow->entry()->isPinnedDialog()) {
|
if (!ofRow || !ofRow->entry()->isPinnedDialog(_filterId)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
for (const auto row : *shownDialogs()) {
|
for (const auto row : *shownDialogs()) {
|
||||||
if (row->entry()->fixedOnTopIndex()) {
|
if (row->entry()->fixedOnTopIndex()) {
|
||||||
continue;
|
continue;
|
||||||
} else if (!row->entry()->isPinnedDialog()) {
|
} else if (!row->entry()->isPinnedDialog(_filterId)) {
|
||||||
break;
|
break;
|
||||||
} else if (row == ofRow) {
|
} else if (row == ofRow) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -1121,7 +1125,9 @@ int InnerWidget::countPinnedIndex(Row *ofRow) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::savePinnedOrder() {
|
void InnerWidget::savePinnedOrder() {
|
||||||
const auto &newOrder = session().data().pinnedChatsOrder(_openedFolder);
|
const auto &newOrder = session().data().pinnedChatsOrder(
|
||||||
|
_openedFolder,
|
||||||
|
_filterId);
|
||||||
if (newOrder.size() != _pinnedOnDragStart.size()) {
|
if (newOrder.size() != _pinnedOnDragStart.size()) {
|
||||||
return; // Something has changed in the set of pinned chats.
|
return; // Something has changed in the set of pinned chats.
|
||||||
}
|
}
|
||||||
|
@ -1130,7 +1136,11 @@ void InnerWidget::savePinnedOrder() {
|
||||||
return; // Something has changed in the set of pinned chats.
|
return; // Something has changed in the set of pinned chats.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
session().api().savePinnedOrder(_openedFolder);
|
if (_filterId) {
|
||||||
|
// #TODO pinned reorder data and to server
|
||||||
|
} else {
|
||||||
|
session().api().savePinnedOrder(_openedFolder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::finishReorderPinned() {
|
void InnerWidget::finishReorderPinned() {
|
||||||
|
@ -1162,7 +1172,7 @@ int InnerWidget::updateReorderIndexGetCount() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto count = Dialogs::PinnedDialogsCount(shownDialogs());
|
const auto count = Dialogs::PinnedDialogsCount(_filterId, shownDialogs());
|
||||||
Assert(index < count);
|
Assert(index < count);
|
||||||
if (count < 2) {
|
if (count < 2) {
|
||||||
stopReorderPinned();
|
stopReorderPinned();
|
||||||
|
@ -1789,6 +1799,7 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
Window::FillPeerMenu(
|
Window::FillPeerMenu(
|
||||||
_controller,
|
_controller,
|
||||||
history->peer,
|
history->peer,
|
||||||
|
_filterId,
|
||||||
[&](const QString &text, Fn<void()> callback) {
|
[&](const QString &text, Fn<void()> callback) {
|
||||||
return _menu->addAction(text, std::move(callback));
|
return _menu->addAction(text, std::move(callback));
|
||||||
},
|
},
|
||||||
|
@ -2541,6 +2552,7 @@ void InnerWidget::switchToFilter(FilterId filterId) {
|
||||||
&Data::ChatFilter::id)) {
|
&Data::ChatFilter::id)) {
|
||||||
filterId = 0;
|
filterId = 0;
|
||||||
}
|
}
|
||||||
|
stopReorderPinned();
|
||||||
_filterId = filterId;
|
_filterId = filterId;
|
||||||
refreshWithCollapsedRows(true);
|
refreshWithCollapsedRows(true);
|
||||||
_collapsedSelected = 0;
|
_collapsedSelected = 0;
|
||||||
|
@ -2983,7 +2995,7 @@ void InnerWidget::setupShortcuts() {
|
||||||
for (const auto [command, index] : pinned) {
|
for (const auto [command, index] : pinned) {
|
||||||
request->check(command) && request->handle([=, index = index] {
|
request->check(command) && request->handle([=, index = index] {
|
||||||
const auto list = session().data().chatsList()->indexed();
|
const auto list = session().data().chatsList()->indexed();
|
||||||
const auto count = Dialogs::PinnedDialogsCount(list);
|
const auto count = Dialogs::PinnedDialogsCount(_filterId, list);
|
||||||
if (index >= count) {
|
if (index >= count) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,6 +218,7 @@ void paintRow(
|
||||||
not_null<const BasicRow*> row,
|
not_null<const BasicRow*> row,
|
||||||
not_null<Entry*> entry,
|
not_null<Entry*> entry,
|
||||||
Dialogs::Key chat,
|
Dialogs::Key chat,
|
||||||
|
FilterId filterId,
|
||||||
PeerData *from,
|
PeerData *from,
|
||||||
const HiddenSenderInfo *hiddenSenderInfo,
|
const HiddenSenderInfo *hiddenSenderInfo,
|
||||||
HistoryItem *item,
|
HistoryItem *item,
|
||||||
|
@ -322,7 +323,7 @@ void paintRow(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) {
|
if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) {
|
||||||
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
|
@ -349,7 +350,7 @@ void paintRow(
|
||||||
}
|
}
|
||||||
} else if (!item) {
|
} else if (!item) {
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) {
|
if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) {
|
||||||
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
|
@ -366,7 +367,7 @@ void paintRow(
|
||||||
}
|
}
|
||||||
|
|
||||||
paintItemCallback(nameleft, namewidth);
|
paintItemCallback(nameleft, namewidth);
|
||||||
} else if (entry->isPinnedDialog() && !entry->fixedOnTopIndex()) {
|
} else if (entry->isPinnedDialog(filterId) && (filterId || !entry->fixedOnTopIndex())) {
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
|
@ -613,7 +614,7 @@ void paintUnreadCount(
|
||||||
void RowPainter::paint(
|
void RowPainter::paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
not_null<const Row*> row,
|
not_null<const Row*> row,
|
||||||
int filterId,
|
FilterId filterId,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
bool active,
|
bool active,
|
||||||
bool selected,
|
bool selected,
|
||||||
|
@ -669,9 +670,8 @@ void RowPainter::paint(
|
||||||
const auto displayPinnedIcon = !displayUnreadCounter
|
const auto displayPinnedIcon = !displayUnreadCounter
|
||||||
&& !displayMentionBadge
|
&& !displayMentionBadge
|
||||||
&& !displayUnreadMark
|
&& !displayUnreadMark
|
||||||
&& !filterId
|
&& entry->isPinnedDialog(filterId)
|
||||||
&& entry->isPinnedDialog()
|
&& (filterId || !entry->fixedOnTopIndex());
|
||||||
&& !entry->fixedOnTopIndex();
|
|
||||||
|
|
||||||
const auto from = history
|
const auto from = history
|
||||||
? (history->peer->migrateTo()
|
? (history->peer->migrateTo()
|
||||||
|
@ -749,6 +749,7 @@ void RowPainter::paint(
|
||||||
row,
|
row,
|
||||||
entry,
|
entry,
|
||||||
row->key(),
|
row->key(),
|
||||||
|
filterId,
|
||||||
from,
|
from,
|
||||||
nullptr,
|
nullptr,
|
||||||
item,
|
item,
|
||||||
|
@ -872,6 +873,7 @@ void RowPainter::paint(
|
||||||
row,
|
row,
|
||||||
history,
|
history,
|
||||||
history,
|
history,
|
||||||
|
FilterId(),
|
||||||
from,
|
from,
|
||||||
hiddenSenderInfo,
|
hiddenSenderInfo,
|
||||||
item,
|
item,
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
static void paint(
|
static void paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
not_null<const Row*> row,
|
not_null<const Row*> row,
|
||||||
int filterId,
|
FilterId filterId,
|
||||||
int fullWidth,
|
int fullWidth,
|
||||||
bool active,
|
bool active,
|
||||||
bool selected,
|
bool selected,
|
||||||
|
|
|
@ -14,7 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
|
||||||
List::List(SortMode sortMode) : _sortMode(sortMode) {
|
List::List(SortMode sortMode, FilterId filterId)
|
||||||
|
: _sortMode(sortMode)
|
||||||
|
, _filterId(filterId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
List::const_iterator List::cfind(Row *value) const {
|
List::const_iterator List::cfind(Row *value) const {
|
||||||
|
@ -32,7 +34,7 @@ not_null<Row*> List::addToEnd(Key key) {
|
||||||
std::make_unique<Row>(key, _rows.size())
|
std::make_unique<Row>(key, _rows.size())
|
||||||
).first->second.get();
|
).first->second.get();
|
||||||
_rows.emplace_back(result);
|
_rows.emplace_back(result);
|
||||||
if (_sortMode == SortMode::Date || _sortMode == SortMode::Complex) {
|
if (_sortMode == SortMode::Date) {
|
||||||
adjustByDate(result);
|
adjustByDate(result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -82,20 +84,20 @@ void List::adjustByName(not_null<Row*> row) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void List::adjustByDate(not_null<Row*> row) {
|
void List::adjustByDate(not_null<Row*> row) {
|
||||||
Expects(_sortMode == SortMode::Date || _sortMode == SortMode::Complex);
|
Expects(_sortMode == SortMode::Date);
|
||||||
|
|
||||||
const auto key = row->sortKey(_sortMode);
|
const auto key = row->sortKey(_filterId);
|
||||||
const auto index = row->pos();
|
const auto index = row->pos();
|
||||||
const auto i = _rows.begin() + index;
|
const auto i = _rows.begin() + index;
|
||||||
const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) {
|
const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) {
|
||||||
return (row->sortKey(_sortMode) <= key);
|
return (row->sortKey(_filterId) <= key);
|
||||||
});
|
});
|
||||||
if (before != i + 1) {
|
if (before != i + 1) {
|
||||||
rotate(i, i + 1, before);
|
rotate(i, i + 1, before);
|
||||||
} else {
|
} else {
|
||||||
const auto from = std::make_reverse_iterator(i);
|
const auto from = std::make_reverse_iterator(i);
|
||||||
const auto after = std::find_if(from, _rows.rend(), [&](Row *row) {
|
const auto after = std::find_if(from, _rows.rend(), [&](Row *row) {
|
||||||
return (row->sortKey(_sortMode) >= key);
|
return (row->sortKey(_filterId) >= key);
|
||||||
}).base();
|
}).base();
|
||||||
if (after != i) {
|
if (after != i) {
|
||||||
rotate(after, i, i + 1);
|
rotate(after, i, i + 1);
|
||||||
|
|
|
@ -16,7 +16,7 @@ enum class SortMode;
|
||||||
|
|
||||||
class List final {
|
class List final {
|
||||||
public:
|
public:
|
||||||
List(SortMode sortMode);
|
List(SortMode sortMode, FilterId filterId = 0);
|
||||||
List(const List &other) = delete;
|
List(const List &other) = delete;
|
||||||
List &operator=(const List &other) = delete;
|
List &operator=(const List &other) = delete;
|
||||||
List(List &&other) = default;
|
List(List &&other) = default;
|
||||||
|
@ -78,6 +78,7 @@ private:
|
||||||
std::vector<not_null<Row*>>::iterator last);
|
std::vector<not_null<Row*>>::iterator last);
|
||||||
|
|
||||||
SortMode _sortMode = SortMode();
|
SortMode _sortMode = SortMode();
|
||||||
|
FilterId _filterId = 0;
|
||||||
std::vector<not_null<Row*>> _rows;
|
std::vector<not_null<Row*>> _rows;
|
||||||
std::map<Key, std::unique_ptr<Row>> _rowByKey;
|
std::map<Key, std::unique_ptr<Row>> _rowByKey;
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ namespace Dialogs {
|
||||||
|
|
||||||
MainList::MainList(FilterId filterId, rpl::producer<int> pinnedLimit)
|
MainList::MainList(FilterId filterId, rpl::producer<int> pinnedLimit)
|
||||||
: _filterId(filterId)
|
: _filterId(filterId)
|
||||||
, _all(filterId ? SortMode::Date : SortMode::Complex)
|
, _all(SortMode::Date, filterId)
|
||||||
, _pinned(1) {
|
, _pinned(filterId, 1) {
|
||||||
_unreadState.known = true;
|
_unreadState.known = true;
|
||||||
|
|
||||||
std::move(
|
std::move(
|
||||||
|
|
|
@ -9,11 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "dialogs/dialogs_key.h"
|
#include "dialogs/dialogs_key.h"
|
||||||
#include "dialogs/dialogs_entry.h"
|
#include "dialogs/dialogs_entry.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
|
||||||
PinnedList::PinnedList(int limit) : _limit(limit) {
|
PinnedList::PinnedList(FilterId filterId, int limit)
|
||||||
|
: _filterId(filterId)
|
||||||
|
, _limit(limit) {
|
||||||
Expects(limit > 0);
|
Expects(limit > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +44,7 @@ int PinnedList::addPinnedGetPosition(const Key &key) {
|
||||||
applyLimit(_limit - 1);
|
applyLimit(_limit - 1);
|
||||||
const auto position = int(_data.size());
|
const auto position = int(_data.size());
|
||||||
_data.push_back(key);
|
_data.push_back(key);
|
||||||
key.entry()->cachePinnedIndex(position + 1);
|
key.entry()->cachePinnedIndex(_filterId, position + 1);
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,19 +57,34 @@ void PinnedList::setPinned(const Key &key, bool pinned) {
|
||||||
const auto begin = _data.begin();
|
const auto begin = _data.begin();
|
||||||
std::rotate(begin, begin + position, begin + position + 1);
|
std::rotate(begin, begin + position, begin + position + 1);
|
||||||
for (auto i = 0; i != position + 1; ++i) {
|
for (auto i = 0; i != position + 1; ++i) {
|
||||||
_data[i].entry()->cachePinnedIndex(i + 1);
|
_data[i].entry()->cachePinnedIndex(_filterId, i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (const auto it = ranges::find(_data, key); it != end(_data)) {
|
} else if (const auto it = ranges::find(_data, key); it != end(_data)) {
|
||||||
const auto index = int(it - begin(_data));
|
const auto index = int(it - begin(_data));
|
||||||
_data.erase(it);
|
_data.erase(it);
|
||||||
key.entry()->cachePinnedIndex(0);
|
key.entry()->cachePinnedIndex(_filterId, 0);
|
||||||
for (auto i = index, count = int(size(_data)); i != count; ++i) {
|
for (auto i = index, count = int(size(_data)); i != count; ++i) {
|
||||||
_data[i].entry()->cachePinnedIndex(i + 1);
|
_data[i].entry()->cachePinnedIndex(_filterId, i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PinnedList::applyFilterPinned(
|
||||||
|
FilterId filterId,
|
||||||
|
not_null<History*> history,
|
||||||
|
int index) {
|
||||||
|
Expects(index > 0);
|
||||||
|
|
||||||
|
history->cachePinnedIndex(filterId, index);
|
||||||
|
|
||||||
|
const auto key = Key{ history };
|
||||||
|
if (ranges::find(_data, key) == end(_data)) {
|
||||||
|
_data.push_back(key);
|
||||||
|
// #TODO pinned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PinnedList::applyLimit(int limit) {
|
void PinnedList::applyLimit(int limit) {
|
||||||
Expects(limit >= 0);
|
Expects(limit >= 0);
|
||||||
|
|
||||||
|
@ -83,18 +101,32 @@ void PinnedList::applyList(
|
||||||
not_null<Data::Session*> owner,
|
not_null<Data::Session*> owner,
|
||||||
const QVector<MTPDialogPeer> &list) {
|
const QVector<MTPDialogPeer> &list) {
|
||||||
clear();
|
clear();
|
||||||
for (const auto &peer : ranges::view::reverse(list)) {
|
for (const auto &peer : list) {
|
||||||
peer.match([&](const MTPDdialogPeer &data) {
|
peer.match([&](const MTPDdialogPeer &data) {
|
||||||
if (const auto peerId = peerFromMTP(data.vpeer())) {
|
if (const auto peerId = peerFromMTP(data.vpeer())) {
|
||||||
setPinned(owner->history(peerId), true);
|
addPinned(owner->history(peerId));
|
||||||
}
|
}
|
||||||
}, [&](const MTPDdialogPeerFolder &data) {
|
}, [&](const MTPDdialogPeerFolder &data) {
|
||||||
const auto folderId = data.vfolder_id().v;
|
addPinned(owner->folder(data.vfolder_id().v));
|
||||||
setPinned(owner->folder(folderId), true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PinnedList::applyList(const std::vector<not_null<History*>> &list) {
|
||||||
|
Expects(_filterId != 0);
|
||||||
|
|
||||||
|
clear();
|
||||||
|
const auto count = int(list.size());
|
||||||
|
_data.reserve(count);
|
||||||
|
for (auto i = 0; i != count; ++i) {
|
||||||
|
const auto history = list[i];
|
||||||
|
if (history->inChatList()) {
|
||||||
|
_data.emplace_back(history);
|
||||||
|
history->cachePinnedIndex(_filterId, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PinnedList::reorder(const Key &key1, const Key &key2) {
|
void PinnedList::reorder(const Key &key1, const Key &key2) {
|
||||||
const auto index1 = ranges::find(_data, key1) - begin(_data);
|
const auto index1 = ranges::find(_data, key1) - begin(_data);
|
||||||
const auto index2 = ranges::find(_data, key2) - begin(_data);
|
const auto index2 = ranges::find(_data, key2) - begin(_data);
|
||||||
|
@ -102,8 +134,8 @@ void PinnedList::reorder(const Key &key1, const Key &key2) {
|
||||||
Assert(index2 >= 0 && index2 < _data.size());
|
Assert(index2 >= 0 && index2 < _data.size());
|
||||||
Assert(index1 != index2);
|
Assert(index1 != index2);
|
||||||
std::swap(_data[index1], _data[index2]);
|
std::swap(_data[index1], _data[index2]);
|
||||||
key1.entry()->cachePinnedIndex(index2 + 1);
|
key1.entry()->cachePinnedIndex(_filterId, index2 + 1);
|
||||||
key2.entry()->cachePinnedIndex(index1 + 1);
|
key2.entry()->cachePinnedIndex(_filterId, index1 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dialogs
|
} // namespace Dialogs
|
||||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
class History;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
@ -17,7 +19,7 @@ class Key;
|
||||||
|
|
||||||
class PinnedList final {
|
class PinnedList final {
|
||||||
public:
|
public:
|
||||||
explicit PinnedList(int limit);
|
PinnedList(FilterId filterId, int limit);
|
||||||
|
|
||||||
void setLimit(int limit);
|
void setLimit(int limit);
|
||||||
|
|
||||||
|
@ -28,11 +30,17 @@ public:
|
||||||
// if (pinned) places on the first place in the list.
|
// if (pinned) places on the first place in the list.
|
||||||
void setPinned(const Key &key, bool pinned);
|
void setPinned(const Key &key, bool pinned);
|
||||||
|
|
||||||
|
void applyFilterPinned(
|
||||||
|
FilterId filterId,
|
||||||
|
not_null<History*> history,
|
||||||
|
int index);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void applyList(
|
void applyList(
|
||||||
not_null<Data::Session*> owner,
|
not_null<Data::Session*> owner,
|
||||||
const QVector<MTPDialogPeer> &list);
|
const QVector<MTPDialogPeer> &list);
|
||||||
|
void applyList(const std::vector<not_null<History*>> &list);
|
||||||
void reorder(const Key &key1, const Key &key2);
|
void reorder(const Key &key1, const Key &key2);
|
||||||
|
|
||||||
const std::vector<Key> &order() const {
|
const std::vector<Key> &order() const {
|
||||||
|
@ -43,6 +51,7 @@ private:
|
||||||
int addPinnedGetPosition(const Key &key);
|
int addPinnedGetPosition(const Key &key);
|
||||||
void applyLimit(int limit);
|
void applyLimit(int limit);
|
||||||
|
|
||||||
|
FilterId _filterId = 0;
|
||||||
int _limit = 0;
|
int _limit = 0;
|
||||||
std::vector<Key> _data;
|
std::vector<Key> _data;
|
||||||
|
|
||||||
|
|
|
@ -220,10 +220,8 @@ Row::Row(Key key, int pos) : _id(key), _pos(pos) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 Row::sortKey(SortMode mode) const {
|
uint64 Row::sortKey(FilterId filterId) const {
|
||||||
return (mode == SortMode::Complex)
|
return _id.entry()->sortKeyInChatList(filterId);
|
||||||
? _id.entry()->sortKeyInChatList()
|
|
||||||
: _id.entry()->sortKeyByDate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Row::validateListEntryCache() const {
|
void Row::validateListEntryCache() const {
|
||||||
|
|
|
@ -90,7 +90,7 @@ public:
|
||||||
int pos() const {
|
int pos() const {
|
||||||
return _pos;
|
return _pos;
|
||||||
}
|
}
|
||||||
uint64 sortKey(SortMode mode) const;
|
uint64 sortKey(FilterId filterId) const;
|
||||||
|
|
||||||
void validateListEntryCache() const;
|
void validateListEntryCache() const;
|
||||||
const Ui::Text::String &listEntryCache() const {
|
const Ui::Text::String &listEntryCache() const {
|
||||||
|
|
|
@ -1921,9 +1921,6 @@ void History::setFolderPointer(Data::Folder *folder) {
|
||||||
if (_folder == folder) {
|
if (_folder == folder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isPinnedDialog()) {
|
|
||||||
owner().setChatPinned(this, false);
|
|
||||||
}
|
|
||||||
auto &filters = owner().chatsFilters();
|
auto &filters = owner().chatsFilters();
|
||||||
const auto wasKnown = folderKnown();
|
const auto wasKnown = folderKnown();
|
||||||
const auto wasInList = inChatList();
|
const auto wasInList = inChatList();
|
||||||
|
@ -1946,6 +1943,7 @@ void History::setFolderPointer(Data::Folder *folder) {
|
||||||
for (const auto &filter : filters.list()) {
|
for (const auto &filter : filters.list()) {
|
||||||
if (filter.contains(this)) {
|
if (filter.contains(this)) {
|
||||||
const auto id = filter.id();
|
const auto id = filter.id();
|
||||||
|
applyFilterPinnedIndex(id, filter);
|
||||||
addToChatList(id, filters.chatsList(id));
|
addToChatList(id, filters.chatsList(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1959,6 +1957,24 @@ void History::setFolderPointer(Data::Folder *folder) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void History::applyFilterPinnedIndex(
|
||||||
|
FilterId filterId,
|
||||||
|
const Data::ChatFilter &filter) {
|
||||||
|
const auto &pinned = filter.pinned();
|
||||||
|
const auto i = ranges::find(pinned, this);
|
||||||
|
if (i == end(pinned)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto index = (i - begin(pinned)) + 1;
|
||||||
|
if (index == lookupPinnedIndex(filterId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
owner().chatsFilters().chatsList(filterId)->pinned()->applyFilterPinned(
|
||||||
|
filterId,
|
||||||
|
this,
|
||||||
|
index);
|
||||||
|
}
|
||||||
|
|
||||||
void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
|
void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
|
||||||
const auto folderId = data.vfolder_id().value_or_empty();
|
const auto folderId = data.vfolder_id().value_or_empty();
|
||||||
if (!folderKnown()) {
|
if (!folderKnown()) {
|
||||||
|
@ -1968,7 +1984,7 @@ void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
|
||||||
clearFolder();
|
clearFolder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
owner().setChatPinned(this, data.is_pinned());
|
owner().setChatPinned(this, FilterId(), data.is_pinned());
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeId History::adjustedChatListTimeId() const {
|
TimeId History::adjustedChatListTimeId() const {
|
||||||
|
@ -2576,7 +2592,7 @@ bool History::useProxyPromotion() const {
|
||||||
if (!isProxyPromoted()) {
|
if (!isProxyPromoted()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (const auto channel = peer->asChannel()) {
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
return !isPinnedDialog() && !channel->amIn();
|
return !isPinnedDialog(FilterId()) && !channel->amIn();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2595,7 +2611,7 @@ bool History::trackUnreadMessages() const {
|
||||||
bool History::shouldBeInChatList() const {
|
bool History::shouldBeInChatList() const {
|
||||||
if (peer->migrateTo() || !folderKnown()) {
|
if (peer->migrateTo() || !folderKnown()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (isPinnedDialog()) {
|
} else if (isPinnedDialog(FilterId())) {
|
||||||
return true;
|
return true;
|
||||||
} else if (const auto channel = peer->asChannel()) {
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
if (!channel->amIn()) {
|
if (!channel->amIn()) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace Data {
|
||||||
struct Draft;
|
struct Draft;
|
||||||
class Session;
|
class Session;
|
||||||
class Folder;
|
class Folder;
|
||||||
|
class ChatFilter;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
@ -368,6 +369,10 @@ public:
|
||||||
HistoryItem *folderDialogItem = nullptr);
|
HistoryItem *folderDialogItem = nullptr);
|
||||||
void clearFolder();
|
void clearFolder();
|
||||||
|
|
||||||
|
void applyFilterPinnedIndex(
|
||||||
|
FilterId filterId,
|
||||||
|
const Data::ChatFilter &filter);
|
||||||
|
|
||||||
// Interface for Data::Histories.
|
// Interface for Data::Histories.
|
||||||
void setInboxReadTill(MsgId upTo);
|
void setInboxReadTill(MsgId upTo);
|
||||||
std::optional<int> countStillUnreadLocal(MsgId readTillId) const;
|
std::optional<int> countStillUnreadLocal(MsgId readTillId) const;
|
||||||
|
|
|
@ -226,6 +226,7 @@ void TopBarWidget::showMenu() {
|
||||||
Window::FillPeerMenu(
|
Window::FillPeerMenu(
|
||||||
_controller,
|
_controller,
|
||||||
peer,
|
peer,
|
||||||
|
FilterId(),
|
||||||
addAction,
|
addAction,
|
||||||
Window::PeerMenuSource::History);
|
Window::PeerMenuSource::History);
|
||||||
} else if (const auto folder = _activeChat.folder()) {
|
} else if (const auto folder = _activeChat.folder()) {
|
||||||
|
|
|
@ -574,6 +574,7 @@ void WrapWidget::showTopBarMenu() {
|
||||||
Window::FillPeerMenu(
|
Window::FillPeerMenu(
|
||||||
_controller->parentController(),
|
_controller->parentController(),
|
||||||
peer,
|
peer,
|
||||||
|
FilterId(),
|
||||||
addAction,
|
addAction,
|
||||||
Window::PeerMenuSource::Profile);
|
Window::PeerMenuSource::Profile);
|
||||||
//} else if (const auto feed = key().feed()) { // #feed
|
//} else if (const auto feed = key().feed()) { // #feed
|
||||||
|
|
|
@ -64,6 +64,7 @@ public:
|
||||||
Filler(
|
Filler(
|
||||||
not_null<SessionController*> controller,
|
not_null<SessionController*> controller,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
FilterId filterId,
|
||||||
const PeerMenuCallback &addAction,
|
const PeerMenuCallback &addAction,
|
||||||
PeerMenuSource source);
|
PeerMenuSource source);
|
||||||
void fill();
|
void fill();
|
||||||
|
@ -84,6 +85,7 @@ private:
|
||||||
|
|
||||||
not_null<SessionController*> _controller;
|
not_null<SessionController*> _controller;
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
|
FilterId _filterId = 0;
|
||||||
const PeerMenuCallback &_addAction;
|
const PeerMenuCallback &_addAction;
|
||||||
PeerMenuSource _source;
|
PeerMenuSource _source;
|
||||||
|
|
||||||
|
@ -115,7 +117,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
History *FindWastedPin(not_null<Data::Session*> data, Data::Folder *folder) {
|
History *FindWastedPin(not_null<Data::Session*> data, Data::Folder *folder) {
|
||||||
const auto &order = data->pinnedChatsOrder(folder);
|
const auto &order = data->pinnedChatsOrder(folder, FilterId());
|
||||||
for (const auto &pinned : order) {
|
for (const auto &pinned : order) {
|
||||||
if (const auto history = pinned.history()) {
|
if (const auto history = pinned.history()) {
|
||||||
if (history->peer->isChat()
|
if (history->peer->isChat()
|
||||||
|
@ -134,24 +136,25 @@ void AddChatMembers(
|
||||||
AddParticipantsBoxController::Start(navigation, chat);
|
AddParticipantsBoxController::Start(navigation, chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PinnedLimitReached(Dialogs::Key key) {
|
bool PinnedLimitReached(Dialogs::Key key, FilterId filterId) {
|
||||||
Expects(key.entry()->folderKnown());
|
Expects(key.entry()->folderKnown());
|
||||||
|
|
||||||
const auto entry = key.entry();
|
const auto entry = key.entry();
|
||||||
const auto owner = &entry->owner();
|
const auto owner = &entry->owner();
|
||||||
const auto folder = entry->folder();
|
const auto folder = entry->folder();
|
||||||
const auto pinnedCount = owner->pinnedChatsCount(folder);
|
const auto pinnedCount = owner->pinnedChatsCount(folder, filterId);
|
||||||
const auto pinnedMax = owner->pinnedChatsLimit(folder);
|
const auto pinnedMax = owner->pinnedChatsLimit(folder, filterId);
|
||||||
if (pinnedCount < pinnedMax) {
|
if (pinnedCount < pinnedMax) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Some old chat, that was converted, maybe is still pinned.
|
// Some old chat, that was converted, maybe is still pinned.
|
||||||
if (const auto wasted = FindWastedPin(owner, folder)) {
|
const auto wasted = filterId ? nullptr : FindWastedPin(owner, folder);
|
||||||
owner->setChatPinned(wasted, false);
|
if (wasted) {
|
||||||
owner->setChatPinned(key, true);
|
owner->setChatPinned(wasted, FilterId(), false);
|
||||||
|
owner->setChatPinned(key, FilterId(), true);
|
||||||
entry->session().api().savePinnedOrder(folder);
|
entry->session().api().savePinnedOrder(folder);
|
||||||
} else {
|
} else {
|
||||||
auto errorText = tr::lng_error_pinned_max(
|
const auto errorText = tr::lng_error_pinned_max(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_count,
|
lt_count,
|
||||||
pinnedMax);
|
pinnedMax);
|
||||||
|
@ -165,12 +168,12 @@ void TogglePinnedDialog(Dialogs::Key key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto owner = &key.entry()->owner();
|
const auto owner = &key.entry()->owner();
|
||||||
const auto isPinned = !key.entry()->isPinnedDialog();
|
const auto isPinned = !key.entry()->isPinnedDialog(0);
|
||||||
if (isPinned && PinnedLimitReached(key)) {
|
if (isPinned && PinnedLimitReached(key, 0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
owner->setChatPinned(key, isPinned);
|
owner->setChatPinned(key, FilterId(), isPinned);
|
||||||
const auto flags = isPinned
|
const auto flags = isPinned
|
||||||
? MTPmessages_ToggleDialogPin::Flag::f_pinned
|
? MTPmessages_ToggleDialogPin::Flag::f_pinned
|
||||||
: MTPmessages_ToggleDialogPin::Flag(0);
|
: MTPmessages_ToggleDialogPin::Flag(0);
|
||||||
|
@ -194,13 +197,50 @@ void TogglePinnedDialog(Dialogs::Key key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TogglePinnedDialog(Dialogs::Key key, FilterId filterId) {
|
||||||
|
if (!filterId) {
|
||||||
|
return TogglePinnedDialog(key);
|
||||||
|
}
|
||||||
|
const auto owner = &key.entry()->owner();
|
||||||
|
const auto isPinned = !key.entry()->isPinnedDialog(filterId);
|
||||||
|
if (isPinned && PinnedLimitReached(key, filterId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
owner->setChatPinned(key, filterId, isPinned);
|
||||||
|
// #TODO pinned save data and to server
|
||||||
|
//const auto flags = isPinned
|
||||||
|
// ? MTPmessages_ToggleDialogPin::Flag::f_pinned
|
||||||
|
// : MTPmessages_ToggleDialogPin::Flag(0);
|
||||||
|
//if (const auto history = key.history()) {
|
||||||
|
// history->session().api().request(MTPmessages_ToggleDialogPin(
|
||||||
|
// MTP_flags(flags),
|
||||||
|
// MTP_inputDialogPeer(key.history()->peer->input)
|
||||||
|
// )).done([=](const MTPBool &result) {
|
||||||
|
// owner->notifyPinnedDialogsOrderUpdated();
|
||||||
|
// }).send();
|
||||||
|
//} else if (const auto folder = key.folder()) {
|
||||||
|
// folder->session().api().request(MTPmessages_ToggleDialogPin(
|
||||||
|
// MTP_flags(flags),
|
||||||
|
// MTP_inputDialogPeerFolder(MTP_int(folder->id()))
|
||||||
|
// )).send();
|
||||||
|
//}
|
||||||
|
if (isPinned) {
|
||||||
|
if (const auto main = App::main()) {
|
||||||
|
main->dialogsToUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Filler::Filler(
|
Filler::Filler(
|
||||||
not_null<SessionController*> controller,
|
not_null<SessionController*> controller,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
FilterId filterId,
|
||||||
const PeerMenuCallback &addAction,
|
const PeerMenuCallback &addAction,
|
||||||
PeerMenuSource source)
|
PeerMenuSource source)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _peer(peer)
|
, _peer(peer)
|
||||||
|
, _filterId(filterId)
|
||||||
, _addAction(addAction)
|
, _addAction(addAction)
|
||||||
, _source(source) {
|
, _source(source) {
|
||||||
}
|
}
|
||||||
|
@ -242,27 +282,29 @@ bool Filler::showTogglePin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filler::addTogglePin() {
|
void Filler::addTogglePin() {
|
||||||
auto peer = _peer;
|
const auto filterId = _filterId;
|
||||||
|
const auto peer = _peer;
|
||||||
auto isPinned = false;
|
auto isPinned = false;
|
||||||
if (auto history = peer->owner().historyLoaded(peer)) {
|
if (const auto history = peer->owner().historyLoaded(peer)) {
|
||||||
isPinned = history->isPinnedDialog();
|
isPinned = history->isPinnedDialog(filterId);
|
||||||
}
|
}
|
||||||
auto pinText = [](bool isPinned) {
|
const auto pinText = [](bool isPinned) {
|
||||||
return isPinned
|
return isPinned
|
||||||
? tr::lng_context_unpin_from_top(tr::now)
|
? tr::lng_context_unpin_from_top(tr::now)
|
||||||
: tr::lng_context_pin_to_top(tr::now);
|
: tr::lng_context_pin_to_top(tr::now);
|
||||||
};
|
};
|
||||||
auto pinToggle = [=] {
|
const auto pinToggle = [=] {
|
||||||
TogglePinnedDialog(peer->owner().history(peer));
|
TogglePinnedDialog(peer->owner().history(peer), filterId);
|
||||||
};
|
};
|
||||||
auto pinAction = _addAction(pinText(isPinned), pinToggle);
|
const auto pinAction = _addAction(pinText(isPinned), pinToggle);
|
||||||
|
|
||||||
const auto lifetime = Ui::CreateChild<rpl::lifetime>(pinAction);
|
const auto lifetime = Ui::CreateChild<rpl::lifetime>(pinAction);
|
||||||
Notify::PeerUpdateViewer(
|
Notify::PeerUpdateViewer(
|
||||||
peer,
|
peer,
|
||||||
Notify::PeerUpdate::Flag::ChatPinnedChanged
|
Notify::PeerUpdate::Flag::ChatPinnedChanged
|
||||||
) | rpl::start_with_next([peer, pinAction, pinText] {
|
) | rpl::start_with_next([=] {
|
||||||
auto isPinned = peer->owner().history(peer)->isPinnedDialog();
|
const auto history = peer->owner().history(peer);
|
||||||
|
const auto isPinned = history->isPinnedDialog(filterId);
|
||||||
pinAction->setText(pinText(isPinned));
|
pinAction->setText(pinText(isPinned));
|
||||||
}, *lifetime);
|
}, *lifetime);
|
||||||
}
|
}
|
||||||
|
@ -1041,9 +1083,10 @@ Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer) {
|
||||||
void FillPeerMenu(
|
void FillPeerMenu(
|
||||||
not_null<SessionController*> controller,
|
not_null<SessionController*> controller,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
FilterId filterId,
|
||||||
const PeerMenuCallback &callback,
|
const PeerMenuCallback &callback,
|
||||||
PeerMenuSource source) {
|
PeerMenuSource source) {
|
||||||
Filler filler(controller, peer, callback, source);
|
Filler filler(controller, peer, filterId, callback, source);
|
||||||
filler.fill();
|
filler.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ using PeerMenuCallback = Fn<QAction*(
|
||||||
void FillPeerMenu(
|
void FillPeerMenu(
|
||||||
not_null<SessionController*> controller,
|
not_null<SessionController*> controller,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
FilterId filterId,
|
||||||
const PeerMenuCallback &addAction,
|
const PeerMenuCallback &addAction,
|
||||||
PeerMenuSource source);
|
PeerMenuSource source);
|
||||||
void FillFolderMenu(
|
void FillFolderMenu(
|
||||||
|
|
Loading…
Reference in New Issue