Implement pinned topics reordering.

This commit is contained in:
John Preston 2022-11-08 17:57:54 +04:00
parent c7741cb62a
commit 8a288476b8
17 changed files with 221 additions and 105 deletions

View file

@ -17,9 +17,7 @@ namespace Api {
void SaveNewFilterPinned( void SaveNewFilterPinned(
not_null<Main::Session*> session, not_null<Main::Session*> session,
FilterId filterId) { FilterId filterId) {
const auto &order = session->data().pinnedChatsOrder( const auto &order = session->data().pinnedChatsOrder(filterId);
nullptr,
filterId);
auto &filters = session->data().chatsFilters(); auto &filters = session->data().chatsFilters();
const auto &filter = filters.applyUpdatedPinned(filterId, order); const auto &filter = filters.applyUpdatedPinned(filterId, order);
session->api().request(MTPmessages_UpdateDialogFilter( session->api().request(MTPmessages_UpdateDialogFilter(

View file

@ -2308,6 +2308,29 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChannelPinnedTopics: { case mtpc_updateChannelPinnedTopics: {
const auto &d = update.c_updateChannelPinnedTopics(); const auto &d = update.c_updateChannelPinnedTopics();
const auto peerId = peerFromChannel(d.vchannel_id());
if (const auto peer = session().data().peerLoaded(peerId)) {
if (const auto forum = peer->forum()) {
const auto done = [&] {
const auto list = d.vorder();
if (!list) {
return false;
}
const auto &order = list->v;
const auto notLoaded = [&](const MTPint &topicId) {
return !forum->topicFor(topicId.v);
};
if (!ranges::none_of(order, notLoaded)) {
return false;
}
session().data().applyPinnedTopics(forum, order);
return true;
}();
if (!done) {
forum->reloadTopics();
}
}
}
} break; } break;
// Pinned message. // Pinned message.

View file

@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_web_page.h" #include "data/data_web_page.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_forum.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_sparse_ids.h" #include "data/data_sparse_ids.h"
#include "data/data_search_controller.h" #include "data/data_search_controller.h"
@ -384,9 +385,7 @@ void ApiWrap::checkChatInvite(
} }
void ApiWrap::savePinnedOrder(Data::Folder *folder) { void ApiWrap::savePinnedOrder(Data::Folder *folder) {
const auto &order = _session->data().pinnedChatsOrder( const auto &order = _session->data().pinnedChatsOrder(folder);
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);
@ -408,6 +407,29 @@ void ApiWrap::savePinnedOrder(Data::Folder *folder) {
)).send(); )).send();
} }
void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
const auto &order = _session->data().pinnedChatsOrder(forum);
const auto input = [](const Dialogs::Key &key) {
if (const auto topic = key.topic()) {
return MTP_int(topic->rootId().bare);
}
Unexpected("Key type in pinnedDialogsOrder().");
};
auto topics = QVector<MTPint>();
topics.reserve(order.size());
ranges::transform(
order,
ranges::back_inserter(topics),
input);
request(MTPchannels_ReorderPinnedForumTopics(
MTP_flags(MTPchannels_ReorderPinnedForumTopics::Flag::f_force),
forum->channel()->inputChannel,
MTP_vector(topics)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
}).send();
}
void ApiWrap::toggleHistoryArchived( void ApiWrap::toggleHistoryArchived(
not_null<History*> history, not_null<History*> history,
bool archived, bool archived,

View file

@ -32,6 +32,7 @@ class WallPaper;
struct ResolvedForwardDraft; struct ResolvedForwardDraft;
enum class DefaultNotify; enum class DefaultNotify;
enum class StickersType : uchar; enum class StickersType : uchar;
class Forum;
class ForumTopic; class ForumTopic;
class Thread; class Thread;
} // namespace Data } // namespace Data
@ -149,6 +150,7 @@ public:
void saveCurrentDraftToCloud(); void saveCurrentDraftToCloud();
void savePinnedOrder(Data::Folder *folder); void savePinnedOrder(Data::Folder *folder);
void savePinnedOrder(not_null<Data::Forum*> forum);
void toggleHistoryArchived( void toggleHistoryArchived(
not_null<History*> history, not_null<History*> history,
bool archived, bool archived,

View file

@ -127,7 +127,7 @@ ChooseFilterValidator::LimitData ChooseFilterValidator::limitReached(
const auto list = _history->owner().chatsFilters().list(); const auto list = _history->owner().chatsFilters().list();
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id); const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
const auto limit = _history->owner().pinnedChatsLimit(nullptr, filterId); const auto limit = _history->owner().pinnedChatsLimit(filterId);
return { return {
.reached = (i != end(list)) .reached = (i != end(list))
&& !ranges::contains(i->always(), _history) && !ranges::contains(i->always(), _history)

View file

@ -236,12 +236,12 @@ not_null<Dialogs::MainList*> ChatFilters::chatsList(FilterId filterId) {
auto limit = rpl::single(rpl::empty_value()) | rpl::then( auto limit = rpl::single(rpl::empty_value()) | rpl::then(
_owner->session().account().appConfig().refreshed() _owner->session().account().appConfig().refreshed()
) | rpl::map([=] { ) | rpl::map([=] {
return _owner->pinnedChatsLimit(nullptr, filterId); return _owner->pinnedChatsLimit(filterId);
}); });
pointer = std::make_unique<Dialogs::MainList>( pointer = std::make_unique<Dialogs::MainList>(
&_owner->session(), &_owner->session(),
filterId, filterId,
_owner->maxPinnedChatsLimitValue(nullptr, filterId)); _owner->maxPinnedChatsLimitValue(filterId));
} }
return pointer.get(); return pointer.get();
} }
@ -476,7 +476,7 @@ const ChatFilter &ChatFilters::applyUpdatedPinned(
const auto i = ranges::find(_list, id, &ChatFilter::id); const auto i = ranges::find(_list, id, &ChatFilter::id);
Assert(i != end(_list)); Assert(i != end(_list));
const auto limit = _owner->pinnedChatsLimit(nullptr, id); const auto limit = _owner->pinnedChatsLimit(id);
auto always = i->always(); auto always = i->always();
auto pinned = std::vector<not_null<History*>>(); auto pinned = std::vector<not_null<History*>>();
pinned.reserve(dialogs.size()); pinned.reserve(dialogs.size());

View file

@ -41,7 +41,7 @@ Folder::Folder(not_null<Data::Session*> owner, FolderId id)
, _chatsList( , _chatsList(
&owner->session(), &owner->session(),
FilterId(), FilterId(),
owner->maxPinnedChatsLimitValue(this, FilterId())) owner->maxPinnedChatsLimitValue(this))
, _name(tr::lng_archived_name(tr::now)) { , _name(tr::lng_archived_name(tr::now)) {
indexNameParts(); indexNameParts();

View file

@ -37,14 +37,13 @@ constexpr auto kTopicsFirstLoad = 20;
constexpr auto kLoadedTopicsMinCount = 20; constexpr auto kLoadedTopicsMinCount = 20;
constexpr auto kTopicsPerPage = 500; constexpr auto kTopicsPerPage = 500;
constexpr auto kStalePerRequest = 100; constexpr auto kStalePerRequest = 100;
constexpr auto kPinnedLimit = 5;
// constexpr auto kGeneralColorId = 0xA9A9A9; // constexpr auto kGeneralColorId = 0xA9A9A9;
} // namespace } // namespace
Forum::Forum(not_null<History*> history) Forum::Forum(not_null<History*> history)
: _history(history) : _history(history)
, _topicsList(&session(), FilterId(0), rpl::single(kPinnedLimit)) { , _topicsList(&session(), {}, owner().maxPinnedChatsLimitValue(this)) {
Expects(_history->peer->isChannel()); Expects(_history->peer->isChannel());
if (_history->inChatList()) { if (_history->inChatList()) {

View file

@ -42,7 +42,7 @@ namespace {
using UpdateFlag = TopicUpdate::Flag; using UpdateFlag = TopicUpdate::Flag;
constexpr auto kUserpicLoopsCount = 2; constexpr auto kUserpicLoopsCount = 1;
} // namespace } // namespace
@ -315,12 +315,7 @@ void ForumTopic::applyTopic(const MTPDforumTopic &data) {
if (min) { if (min) {
int a = 0; int a = 0;
} else { } else {
if (data.is_pinned()) { owner().setPinnedFromEntryList(this, data.is_pinned());
owner().setChatPinned(this, 0, true);
} else {
_list->pinned()->setPinned(this, false);
}
owner().notifySettings().apply(this, data.vnotify_settings()); owner().notifySettings().apply(this, data.vnotify_settings());
if (const auto draft = data.vdraft()) { if (const auto draft = data.vdraft()) {

View file

@ -85,6 +85,8 @@ namespace {
using ViewElement = HistoryView::Element; using ViewElement = HistoryView::Element;
constexpr auto kTopicsPinLimit = 5;
// s: box 100x100 // s: box 100x100
// m: box 320x320 // m: box 320x320
// x: box 800x800 // x: box 800x800
@ -241,7 +243,7 @@ Session::Session(not_null<Main::Session*> session)
, _chatsList( , _chatsList(
session, session,
FilterId(), FilterId(),
maxPinnedChatsLimitValue(nullptr, FilterId())) maxPinnedChatsLimitValue(nullptr))
, _contactsList(Dialogs::SortMode::Name) , _contactsList(Dialogs::SortMode::Name)
, _contactsNoChatsList(Dialogs::SortMode::Name) , _contactsNoChatsList(Dialogs::SortMode::Name)
, _ttlCheckTimer([=] { checkTTLs(); }) , _ttlCheckTimer([=] { checkTTLs(); })
@ -1925,10 +1927,10 @@ void Session::setChatPinned(
notifyPinnedDialogsOrderUpdated(); notifyPinnedDialogsOrderUpdated();
} }
void Session::setPinnedFromDialog(const Dialogs::Key &key, bool pinned) { void Session::setPinnedFromEntryList(const Dialogs::Key &key, bool pinned) {
Expects(key.entry()->folderKnown()); Expects(key.entry()->folderKnown());
const auto list = chatsList(key.entry()->folder())->pinned(); const auto list = chatsListFor(key.entry())->pinned();
if (pinned) { if (pinned) {
list->addPinned(key); list->addPinned(key);
} else { } else {
@ -1957,6 +1959,13 @@ void Session::applyPinnedChats(
notifyPinnedDialogsOrderUpdated(); notifyPinnedDialogsOrderUpdated();
} }
void Session::applyPinnedTopics(
not_null<Data::Forum*> forum,
const QVector<MTPint> &list) {
forum->topicsList()->pinned()->applyList(forum, list);
notifyPinnedDialogsOrderUpdated();
}
void Session::applyDialogs( void Session::applyDialogs(
Data::Folder *requestFolder, Data::Folder *requestFolder,
const QVector<MTPMessage> &messages, const QVector<MTPMessage> &messages,
@ -1983,7 +1992,7 @@ void Session::applyDialog(
const auto history = this->history(peerId); const auto history = this->history(peerId);
history->applyDialog(requestFolder, data); history->applyDialog(requestFolder, data);
setPinnedFromDialog(history, data.is_pinned()); setPinnedFromEntryList(history, data.is_pinned());
if (const auto from = history->peer->migrateFrom()) { if (const auto from = history->peer->migrateFrom()) {
if (const auto historyFrom = historyLoaded(from)) { if (const auto historyFrom = historyLoaded(from)) {
@ -2004,37 +2013,63 @@ void Session::applyDialog(
} }
const auto folder = processFolder(data.vfolder()); const auto folder = processFolder(data.vfolder());
folder->applyDialog(data); folder->applyDialog(data);
setPinnedFromDialog(folder, data.is_pinned()); setPinnedFromEntryList(folder, data.is_pinned());
} }
int Session::pinnedCanPin( bool Session::pinnedCanPin(not_null<Data::Thread*> thread) const {
Data::Folder *folder, if (const auto topic = thread->asTopic()) {
const auto forum = topic->forum();
return pinnedChatsOrder(forum).size() < pinnedChatsLimit(forum);
}
const auto folder = thread->folder();
return pinnedChatsOrder(folder).size() < pinnedChatsLimit(folder);
}
bool Session::pinnedCanPin(
FilterId filterId, FilterId filterId,
not_null<History*> history) const { not_null<History*> history) const {
if (!filterId) { Expects(filterId != 0);
const auto limit = pinnedChatsLimit(folder, filterId);
return pinnedChatsOrder(folder, FilterId()).size() < limit;
}
const auto &list = chatsFilters().list(); const auto &list = chatsFilters().list();
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id); const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
return (i == end(list)) return (i == end(list))
|| (i->always().contains(history)) || (i->always().contains(history))
|| (i->always().size() < pinnedChatsLimit(folder, filterId)); || (i->always().size() < pinnedChatsLimit(filterId));
} }
int Session::pinnedChatsLimit( int Session::pinnedChatsLimit(Data::Folder *folder) const {
Data::Folder *folder,
FilterId filterId) const {
const auto limits = Data::PremiumLimits(_session); const auto limits = Data::PremiumLimits(_session);
return filterId return folder
? limits.dialogFiltersChatsCurrent()
: folder
? limits.dialogsFolderPinnedCurrent() ? limits.dialogsFolderPinnedCurrent()
: limits.dialogsPinnedCurrent(); : limits.dialogsPinnedCurrent();
} }
int Session::pinnedChatsLimit(FilterId filterId) const {
const auto limits = Data::PremiumLimits(_session);
return limits.dialogFiltersChatsCurrent();
}
int Session::pinnedChatsLimit(not_null<Data::Forum*> forum) const {
return kTopicsPinLimit;
}
rpl::producer<int> Session::maxPinnedChatsLimitValue(
Data::Folder *folder) const {
// Premium limit from appconfig.
// We always use premium limit in the MainList limit producer,
// because it slices the list to that limit. We don't want to slice
// premium-ly added chats from the pinned list because of sync issues.
return rpl::single(rpl::empty_value()) | rpl::then(
_session->account().appConfig().refreshed()
) | rpl::map([=] {
const auto limits = Data::PremiumLimits(_session);
return folder
? limits.dialogsFolderPinnedPremium()
: limits.dialogsPinnedPremium();
});
}
rpl::producer<int> Session::maxPinnedChatsLimitValue( rpl::producer<int> Session::maxPinnedChatsLimitValue(
Data::Folder *folder,
FilterId filterId) const { FilterId filterId) const {
// Premium limit from appconfig. // Premium limit from appconfig.
// We always use premium limit in the MainList limit producer, // We always use premium limit in the MainList limit producer,
@ -2044,21 +2079,28 @@ rpl::producer<int> Session::maxPinnedChatsLimitValue(
_session->account().appConfig().refreshed() _session->account().appConfig().refreshed()
) | rpl::map([=] { ) | rpl::map([=] {
const auto limits = Data::PremiumLimits(_session); const auto limits = Data::PremiumLimits(_session);
return filterId return limits.dialogFiltersChatsPremium();
? limits.dialogFiltersChatsPremium()
: folder
? limits.dialogsFolderPinnedPremium()
: limits.dialogsPinnedPremium();
}); });
} }
rpl::producer<int> Session::maxPinnedChatsLimitValue(
not_null<Data::Forum*> forum) const {
return rpl::single(pinnedChatsLimit(forum));
}
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
Data::Folder *folder) const {
return chatsList(folder)->pinned()->order();
}
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder( const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
Data::Folder *folder,
FilterId filterId) const { FilterId filterId) const {
const auto list = filterId return chatsFilters().chatsList(filterId)->pinned()->order();
? chatsFilters().chatsList(filterId) }
: chatsList(folder);
return list->pinned()->order(); const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
not_null<Data::Forum*> forum) const {
return forum->topicsList()->pinned()->order();
} }
void Session::clearPinnedChats(Data::Folder *folder) { void Session::clearPinnedChats(Data::Folder *folder) {
@ -2072,7 +2114,10 @@ void Session::reorderTwoPinnedChats(
Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown()); Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown());
Expects(filterId || (key1.entry()->folder() == key2.entry()->folder())); Expects(filterId || (key1.entry()->folder() == key2.entry()->folder()));
const auto list = filterId const auto topic = key1.topic();
const auto list = topic
? topic->forum()->topicsList()
: filterId
? chatsFilters().chatsList(filterId) ? chatsFilters().chatsList(filterId)
: chatsList(key1.entry()->folder()); : chatsList(key1.entry()->folder());
list->pinned()->reorder(key1, key2); list->pinned()->reorder(key1, key2);

View file

@ -230,16 +230,16 @@ public:
[[nodiscard]] rpl::variable<bool> &contactsLoaded() { [[nodiscard]] rpl::variable<bool> &contactsLoaded() {
return _contactsLoaded; return _contactsLoaded;
} }
[[nodiscard]] rpl::producer<Data::Folder*> chatsListChanges() const { [[nodiscard]] rpl::producer<Folder*> chatsListChanges() const {
return _chatsListChanged.events(); return _chatsListChanged.events();
} }
[[nodiscard]] bool chatsListLoaded(Data::Folder *folder = nullptr); [[nodiscard]] bool chatsListLoaded(Folder *folder = nullptr);
[[nodiscard]] rpl::producer<Data::Folder*> chatsListLoadedEvents() const { [[nodiscard]] rpl::producer<Folder*> chatsListLoadedEvents() const {
return _chatsListLoadedEvents.events(); return _chatsListLoadedEvents.events();
} }
void chatsListChanged(FolderId folderId); void chatsListChanged(FolderId folderId);
void chatsListChanged(Data::Folder *folder); void chatsListChanged(Folder *folder);
void chatsListDone(Data::Folder *folder); void chatsListDone(Folder *folder);
void userIsBotChanged(not_null<UserData*> user); void userIsBotChanged(not_null<UserData*> user);
[[nodiscard]] rpl::producer<not_null<UserData*>> userIsBotChanges() const; [[nodiscard]] rpl::producer<not_null<UserData*>> userIsBotChanges() const;
@ -339,32 +339,42 @@ public:
void applyUpdate(const MTPDupdateChatDefaultBannedRights &update); void applyUpdate(const MTPDupdateChatDefaultBannedRights &update);
void applyDialogs( void applyDialogs(
Data::Folder *requestFolder, Folder *requestFolder,
const QVector<MTPMessage> &messages, const QVector<MTPMessage> &messages,
const QVector<MTPDialog> &dialogs, const QVector<MTPDialog> &dialogs,
std::optional<int> count = std::nullopt); std::optional<int> count = std::nullopt);
int pinnedCanPin( [[nodiscard]] bool pinnedCanPin(not_null<Thread*> thread) const;
Data::Folder *folder, [[nodiscard]] bool pinnedCanPin(
FilterId filterId, FilterId filterId,
not_null<History*> history) const; not_null<History*> history) const;
int pinnedChatsLimit( [[nodiscard]] int pinnedChatsLimit(Folder *folder) const;
Data::Folder *folder, [[nodiscard]] int pinnedChatsLimit(FilterId filterId) const;
[[nodiscard]] int pinnedChatsLimit(not_null<Forum*> forum) const;
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
Folder *folder) const;
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
FilterId filterId) const; FilterId filterId) const;
rpl::producer<int> maxPinnedChatsLimitValue( [[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
Data::Folder *folder, not_null<Forum*> forum) const;
FilterId filterId) const; [[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
const std::vector<Dialogs::Key> &pinnedChatsOrder( Folder *folder) const;
Data::Folder *folder, [[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
not_null<Forum*> forum) const;
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
FilterId filterId) const; FilterId filterId) const;
void setChatPinned( void setChatPinned(
const Dialogs::Key &key, const Dialogs::Key &key,
FilterId filterId, FilterId filterId,
bool pinned); bool pinned);
void clearPinnedChats(Data::Folder *folder); void setPinnedFromEntryList(const Dialogs::Key &key, bool pinned);
void clearPinnedChats(Folder *folder);
void applyPinnedChats( void applyPinnedChats(
Data::Folder *folder, Folder *folder,
const QVector<MTPDialogPeer> &list); const QVector<MTPDialogPeer> &list);
void applyPinnedTopics(
not_null<Data::Forum*> forum,
const QVector<MTPint> &list);
void reorderTwoPinnedChats( void reorderTwoPinnedChats(
FilterId filterId, FilterId filterId,
const Dialogs::Key &key1, const Dialogs::Key &key1,
@ -572,7 +582,7 @@ public:
not_null<PollData*> processPoll(const MTPPoll &data); not_null<PollData*> processPoll(const MTPPoll &data);
not_null<PollData*> processPoll(const MTPDmessageMediaPoll &data); not_null<PollData*> processPoll(const MTPDmessageMediaPoll &data);
[[nodiscard]] not_null<Data::CloudImage*> location( [[nodiscard]] not_null<CloudImage*> location(
const LocationPoint &point); const LocationPoint &point);
void registerPhotoItem( void registerPhotoItem(
@ -655,9 +665,9 @@ public:
[[nodiscard]] not_null<Dialogs::MainList*> chatsListFor( [[nodiscard]] not_null<Dialogs::MainList*> chatsListFor(
not_null<Dialogs::Entry*> entry); not_null<Dialogs::Entry*> entry);
[[nodiscard]] not_null<Dialogs::MainList*> chatsList( [[nodiscard]] not_null<Dialogs::MainList*> chatsList(
Data::Folder *folder = nullptr); Folder *folder = nullptr);
[[nodiscard]] not_null<const Dialogs::MainList*> chatsList( [[nodiscard]] not_null<const Dialogs::MainList*> chatsList(
Data::Folder *folder = nullptr) const; Folder *folder = nullptr) const;
[[nodiscard]] not_null<Dialogs::IndexedList*> contactsList(); [[nodiscard]] not_null<Dialogs::IndexedList*> contactsList();
[[nodiscard]] not_null<Dialogs::IndexedList*> contactsNoChatsList(); [[nodiscard]] not_null<Dialogs::IndexedList*> contactsNoChatsList();
@ -727,9 +737,9 @@ private:
int computeUnreadBadge(const Dialogs::UnreadState &state) const; int computeUnreadBadge(const Dialogs::UnreadState &state) const;
bool computeUnreadBadgeMuted(const Dialogs::UnreadState &state) const; bool computeUnreadBadgeMuted(const Dialogs::UnreadState &state) const;
void applyDialog(Data::Folder *requestFolder, const MTPDdialog &data); void applyDialog(Folder *requestFolder, const MTPDdialog &data);
void applyDialog( void applyDialog(
Data::Folder *requestFolder, Folder *requestFolder,
const MTPDdialogFolder &data); const MTPDdialogFolder &data);
const Messages *messagesList(PeerId peerId) const; const Messages *messagesList(PeerId peerId) const;
@ -818,8 +828,6 @@ private:
PhotoData *photo, PhotoData *photo,
DocumentData *document); DocumentData *document);
void setPinnedFromDialog(const Dialogs::Key &key, bool pinned);
template <typename Method> template <typename Method>
void enumerateItemViews( void enumerateItemViews(
not_null<const HistoryItem*> item, not_null<const HistoryItem*> item,
@ -843,8 +851,8 @@ private:
QPointer<Ui::BoxContent> _exportSuggestion; QPointer<Ui::BoxContent> _exportSuggestion;
rpl::variable<bool> _contactsLoaded = false; rpl::variable<bool> _contactsLoaded = false;
rpl::event_stream<Data::Folder*> _chatsListLoadedEvents; rpl::event_stream<Folder*> _chatsListLoadedEvents;
rpl::event_stream<Data::Folder*> _chatsListChanged; rpl::event_stream<Folder*> _chatsListChanged;
rpl::event_stream<not_null<UserData*>> _userIsBotChanges; rpl::event_stream<not_null<UserData*>> _userIsBotChanges;
rpl::event_stream<not_null<PeerData*>> _botCommandsChanges; rpl::event_stream<not_null<PeerData*>> _botCommandsChanges;
rpl::event_stream<ItemVisibilityQuery> _itemVisibilityQueries; rpl::event_stream<ItemVisibilityQuery> _itemVisibilityQueries;
@ -916,7 +924,7 @@ private:
base::flat_set<not_null<ViewElement*>>> _webpageViews; base::flat_set<not_null<ViewElement*>>> _webpageViews;
std::unordered_map< std::unordered_map<
LocationPoint, LocationPoint,
std::unique_ptr<Data::CloudImage>> _locations; std::unique_ptr<CloudImage>> _locations;
std::unordered_map< std::unordered_map<
PollId, PollId,
std::unique_ptr<PollData>> _polls; std::unique_ptr<PollData>> _polls;

View file

@ -1255,6 +1255,13 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
mousePressReleased(e->globalPos(), e->button(), e->modifiers()); mousePressReleased(e->globalPos(), e->button(), e->modifiers());
} }
} }
const std::vector<Key> &InnerWidget::pinnedChatsOrder() const {
return _openedForum
? session().data().pinnedChatsOrder(_openedForum)
: _filterId
? session().data().pinnedChatsOrder(_filterId)
: session().data().pinnedChatsOrder(_openedFolder);
}
void InnerWidget::checkReorderPinnedStart(QPoint localPosition) { void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
if (!_pressed || _dragging || _state != WidgetState::Default) { if (!_pressed || _dragging || _state != WidgetState::Default) {
@ -1262,16 +1269,12 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
} else if (qAbs(localPosition.y() - _dragStart.y()) } else if (qAbs(localPosition.y() - _dragStart.y())
< style::ConvertScale(kStartReorderThreshold)) { < style::ConvertScale(kStartReorderThreshold)) {
return; return;
} else if (_openedForum) {
return;
} }
_dragging = _pressed; _dragging = _pressed;
if (updateReorderIndexGetCount() < 2) { if (updateReorderIndexGetCount() < 2) {
_dragging = nullptr; _dragging = nullptr;
} else { } else {
const auto &order = session().data().pinnedChatsOrder( const auto &order = pinnedChatsOrder();
_openedFolder,
_filterId);
_pinnedOnDragStart = base::flat_set<Key>{ _pinnedOnDragStart = base::flat_set<Key>{
order.begin(), order.begin(),
order.end() order.end()
@ -1301,12 +1304,7 @@ int InnerWidget::countPinnedIndex(Row *ofRow) {
} }
void InnerWidget::savePinnedOrder() { void InnerWidget::savePinnedOrder() {
if (_openedForum) { const auto &newOrder = pinnedChatsOrder();
return;
}
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.
} }
@ -1315,7 +1313,9 @@ void InnerWidget::savePinnedOrder() {
return; // Something has changed in the set of pinned chats. return; // Something has changed in the set of pinned chats.
} }
} }
if (_filterId) { if (_openedForum) {
session().api().savePinnedOrder(_openedForum);
} else if (_filterId) {
Api::SaveNewFilterPinned(&session(), _filterId); Api::SaveNewFilterPinned(&session(), _filterId);
} else { } else {
session().api().savePinnedOrder(_openedFolder); session().api().savePinnedOrder(_openedFolder);

View file

@ -346,6 +346,7 @@ private:
[[nodiscard]] not_null<IndexedList*> shownDialogs() const; [[nodiscard]] not_null<IndexedList*> shownDialogs() const;
[[nodiscard]] const std::vector<Key> &pinnedChatsOrder() const;
void checkReorderPinnedStart(QPoint localPosition); void checkReorderPinnedStart(QPoint localPosition);
int updateReorderIndexGetCount(); int updateReorderIndexGetCount();
bool updateReorderPinned(QPoint localPosition); bool updateReorderPinned(QPoint localPosition);

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_entry.h" #include "dialogs/dialogs_entry.h"
#include "history/history.h" #include "history/history.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_forum.h"
namespace Dialogs { namespace Dialogs {
@ -97,6 +98,15 @@ void PinnedList::applyList(
} }
} }
void PinnedList::applyList(
not_null<Data::Forum*> forum,
const QVector<MTPint> &list) {
clear();
for (const auto &topicId : list) {
addPinned(forum->topicFor(topicId.v));
}
}
void PinnedList::applyList(const std::vector<not_null<History*>> &list) { void PinnedList::applyList(const std::vector<not_null<History*>> &list) {
Expects(_filterId != 0); Expects(_filterId != 0);

View file

@ -11,6 +11,7 @@ class History;
namespace Data { namespace Data {
class Session; class Session;
class Forum;
} // namespace Data } // namespace Data
namespace Dialogs { namespace Dialogs {
@ -35,6 +36,9 @@ public:
void applyList( void applyList(
not_null<Data::Session*> owner, not_null<Data::Session*> owner,
const QVector<MTPDialogPeer> &list); const QVector<MTPDialogPeer> &list);
void applyList(
not_null<Data::Forum*> forum,
const QVector<MTPint> &list);
void applyList(const std::vector<not_null<History*>> &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);

View file

@ -386,9 +386,7 @@ TimeId CalculateOnlineTill(not_null<PeerData*> peer) {
if (index == result) { if (index == result) {
return; return;
} }
const auto &order = _session->data().pinnedChatsOrder( const auto &order = _session->data().pinnedChatsOrder(nullptr);
nullptr,
FilterId());
const auto d = (index < result) ? 1 : -1; // Direction. const auto d = (index < result) ? 1 : -1; // Direction.
for (auto i = index; i != result; i += d) { for (auto i = index; i != result; i += d) {
_session->data().chatsList()->pinned()->reorder( _session->data().chatsList()->pinned()->reorder(
@ -634,7 +632,7 @@ TimeId CalculateOnlineTill(not_null<PeerData*> peer) {
const auto updatePinnedChats = [=] { const auto updatePinnedChats = [=] {
_pins = ranges::views::zip( _pins = ranges::views::zip(
_session->data().pinnedChatsOrder(nullptr, FilterId()), _session->data().pinnedChatsOrder(nullptr),
ranges::views::ints(0, ranges::unreachable) ranges::views::ints(0, ranges::unreachable)
) | ranges::views::transform([=](const auto &pair) { ) | ranges::views::transform([=](const auto &pair) {
const auto index = pair.second; const auto index = pair.second;

View file

@ -315,7 +315,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, FilterId()); const auto &order = data->pinnedChatsOrder(folder);
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()
@ -336,24 +336,22 @@ void AddChatMembers(
bool PinnedLimitReached( bool PinnedLimitReached(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<History*> history, not_null<Data::Thread*> thread) {
FilterId filterId) { const auto owner = &thread->owner();
Expects(filterId != 0 || history->folderKnown()); if (owner->pinnedCanPin(thread)) {
const auto owner = &history->owner();
const auto folder = history->folder();
if (owner->pinnedCanPin(folder, filterId, history)) {
return false; return false;
} }
// Some old chat, that was converted, maybe is still pinned. // Some old chat, that was converted, maybe is still pinned.
const auto wasted = filterId ? nullptr : FindWastedPin(owner, folder); const auto history = thread->asHistory();
if (!history) {
return true;
}
const auto folder = history->folder();
const auto wasted = FindWastedPin(owner, folder);
if (wasted) { if (wasted) {
owner->setChatPinned(wasted, FilterId(), false); owner->setChatPinned(wasted, FilterId(), false);
owner->setChatPinned(history, FilterId(), true); owner->setChatPinned(history, FilterId(), true);
history->session().api().savePinnedOrder(folder); history->session().api().savePinnedOrder(folder);
} else if (filterId) {
controller->show(
Box(FilterPinsLimitBox, &history->session(), filterId));
} else if (folder) { } else if (folder) {
controller->show(Box(FolderPinsLimitBox, &history->session())); controller->show(Box(FolderPinsLimitBox, &history->session()));
} else { } else {
@ -362,6 +360,19 @@ bool PinnedLimitReached(
return true; return true;
} }
bool PinnedLimitReached(
not_null<Window::SessionController*> controller,
not_null<History*> history,
FilterId filterId) {
const auto owner = &history->owner();
if (owner->pinnedCanPin(filterId, history)) {
return false;
}
controller->show(
Box(FilterPinsLimitBox, &history->session(), filterId));
return true;
}
void TogglePinnedThread( void TogglePinnedThread(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<Data::Thread*> thread) { not_null<Data::Thread*> thread) {