Support pinned chats in folders.

This commit is contained in:
John Preston 2019-04-19 12:47:49 +04:00
parent 607655941d
commit 58519300ea
23 changed files with 564 additions and 304 deletions

View File

@ -433,21 +433,24 @@ void ApiWrap::applyUpdates(
App::main()->feedUpdates(updates, sentMessageRandomId);
}
void ApiWrap::savePinnedOrder() {
const auto &order = _session->data().pinnedDialogsOrder();
void ApiWrap::savePinnedOrder(FolderId folderId) {
const auto &order = _session->data().pinnedChatsOrder(folderId);
const auto input = [](const Dialogs::Key &key) {
if (const auto history = key.history()) {
return MTP_inputDialogPeer(history->peer->input);
} else if (const auto folder = key.folder()) {
return MTP_inputDialogPeerFolder(MTP_int(folder->id()));
}
Unexpected("Key type in pinnedDialogsOrder().");
};
auto peers = QVector<MTPInputDialogPeer>();
peers.reserve(order.size());
for (const auto &pinned : ranges::view::reverse(order)) {
if (const auto history = pinned.history()) {
peers.push_back(MTP_inputDialogPeer(history->peer->input));
// } else if (const auto feed = pinned.feed()) { // #feed
// peers.push_back(MTP_inputDialogPeerFeed(MTP_int(feed->id())));
}
}
const auto folderId = 0;
const auto flags = MTPmessages_ReorderPinnedDialogs::Flag::f_force;
ranges::transform(
order,
ranges::back_inserter(peers),
input);
request(MTPmessages_ReorderPinnedDialogs(
MTP_flags(flags),
MTP_flags(MTPmessages_ReorderPinnedDialogs::Flag::f_force),
MTP_int(folderId),
MTP_vector(peers)
)).send();
@ -704,12 +707,8 @@ void ApiWrap::requestContacts() {
}).send();
}
void ApiWrap::requestDialogs() {
requestMoreDialogs(FolderId(0));
}
void ApiWrap::requestFolderDialogs(FolderId folderId) {
if (!_foldersLoadState.contains(folderId)) {
void ApiWrap::requestDialogs(FolderId folderId) {
if (folderId && !_foldersLoadState.contains(folderId)) {
_foldersLoadState.emplace(folderId, DialogsLoadState());
}
requestMoreDialogs(folderId);
@ -743,12 +742,16 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
: MTP_inputPeerEmpty()),
MTP_int(loadCount),
MTP_int(hash)
)).done([=](const MTPmessages_Dialogs & result) {
)).done([=](const MTPmessages_Dialogs &result) {
const auto state = dialogsLoadState(folderId);
result.match([](const MTPDmessages_dialogsNotModified & data) {
LOG(("API Error: not-modified received for requested dialogs."));
}, [&](const auto & data) {
}, [&](const auto &data) {
if constexpr (data.Is<MTPDmessages_dialogs>()) {
dialogsLoadFinish(folderId);
if (state) {
state->listReceived = true;
dialogsLoadFinish(folderId); // may kill 'state'.
}
} else {
updateDialogsOffset(
folderId,
@ -764,9 +767,11 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
});
if (!folderId) {
requestDialogs();
requestDialogs(folderId);
requestContacts();
if (!_dialogsLoadState || !_dialogsLoadState->requestId) {
if (!_dialogsLoadState
|| (!_dialogsLoadState->listReceived
&& !_dialogsLoadState->requestId)) {
refreshDialogsLoadBlocked();
}
}
@ -774,16 +779,21 @@ void ApiWrap::requestMoreDialogs(FolderId folderId) {
}).fail([=](const RPCError &error) {
dialogsLoadState(folderId)->requestId = 0;
}).send();
if (!_pinnedDialogsReceived) {
requestPinnedDialogs();
if (!state->pinnedReceived) {
requestPinnedDialogs(folderId);
}
if (!folderId) {
refreshDialogsLoadBlocked();
}
refreshDialogsLoadBlocked();
}
void ApiWrap::refreshDialogsLoadBlocked() {
_dialogsLoadMayBlockByDate = _dialogsLoadState
&& !_dialogsLoadState->listReceived
&& (_dialogsLoadTill > 0);
_dialogsLoadBlockedByDate = _dialogsLoadState
&& !_dialogsLoadState->listReceived
&& !_dialogsLoadState->requestId
&& (_dialogsLoadTill > 0)
&& (_dialogsLoadState->offsetDate > 0)
@ -824,15 +834,16 @@ void ApiWrap::updateDialogsOffset(
break;
}
}
if (lastDate) {
if (const auto state = dialogsLoadState(folderId)) {
if (const auto state = dialogsLoadState(folderId)) {
if (lastDate) {
state->offsetDate = lastDate;
state->offsetId = lastMsgId;
state->offsetPeer = _session->data().peer(lastPeer);
state->requestId = 0;
} else {
state->listReceived = true;
dialogsLoadFinish(folderId);
}
} else {
dialogsLoadFinish(folderId);
}
}
@ -850,47 +861,48 @@ void ApiWrap::dialogsLoadFinish(FolderId folderId) {
_session->data().chatsListDone(folderId);
}));
};
const auto state = dialogsLoadState(folderId);
if (!state || !state->listReceived || !state->pinnedReceived) {
return;
}
if (folderId) {
_foldersLoadState.remove(folderId);
notify();
} else {
_dialogsLoadState = nullptr;
if (_pinnedDialogsReceived) {
notify();
}
notify();
}
}
void ApiWrap::requestPinnedDialogs() {
if (_pinnedDialogsRequestId) {
void ApiWrap::requestPinnedDialogs(FolderId folderId) {
const auto state = dialogsLoadState(folderId);
if (!state || state->pinnedReceived || state->pinnedRequestId) {
return;
}
const auto folderId = FolderId(0);
_pinnedDialogsRequestId = request(MTPmessages_GetPinnedDialogs(
const auto finalize = [=] {
if (const auto state = dialogsLoadState(folderId)) {
state->pinnedRequestId = 0;
state->pinnedReceived = true;
dialogsLoadFinish(folderId);
}
};
state->pinnedRequestId = request(MTPmessages_GetPinnedDialogs(
MTP_int(folderId)
)).done([=](const MTPmessages_PeerDialogs &result) {
finalize();
result.match([&](const MTPDmessages_peerDialogs &data) {
const auto folderId = FolderId(0);
_session->data().processUsers(data.vusers);
_session->data().processChats(data.vchats);
_session->data().clearPinnedDialogs();
_session->data().clearPinnedChats(folderId);
_session->data().applyDialogs(
folderId,
data.vmessages.v,
data.vdialogs.v);
_pinnedDialogsRequestId = 0;
_pinnedDialogsReceived = true;
_session->data().chatsListChanged(folderId);
if (!_dialogsLoadState) {
_session->data().chatsListDone(folderId);
}
});
}).fail([=](const RPCError &error) {
_pinnedDialogsRequestId = 0;
finalize();
}).send();
}
@ -902,7 +914,7 @@ void ApiWrap::requestMoreBlockedByDateDialogs() {
_dialogsLoadTill = _dialogsLoadState->offsetDate
? (_dialogsLoadState->offsetDate - max)
: (unixtime() - max);
requestDialogs();
requestDialogs(FolderId(0));
}
rpl::producer<bool> ApiWrap::dialogsLoadMayBlockByDate() const {
@ -932,29 +944,6 @@ void ApiWrap::requestDialogEntry(not_null<Data::Folder*> folder) {
}).send();
}
//void ApiWrap::requestFeedDialogsEntries(not_null<Data::Feed*> feed) {
// if (_dialogFeedRequests.contains(feed)) {
// return;
// }
// _dialogFeedRequests.emplace(feed);
//
// const auto hash = 0;
// request(MTPmessages_GetDialogs(
// MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id),
// MTP_int(feed->id()),
// MTP_int(0), // offset_date
// MTP_int(0), // offset_id
// MTP_inputPeerEmpty(), // offset_peer
// MTP_int(Data::Feed::kChannelsLimit),
// MTP_int(hash)
// )).done([=](const MTPmessages_Dialogs &result) {
// applyFeedDialogs(feed, result);
// _dialogFeedRequests.remove(feed);
// }).fail([=](const RPCError &error) {
// _dialogFeedRequests.remove(feed);
// }).send();
//}
void ApiWrap::requestDialogEntry(
not_null<History*> history,
Fn<void()> callback) {
@ -1054,45 +1043,6 @@ void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) {
}
_session->data().sendHistoryChangeNotifications();
}
// // #feed
//void ApiWrap::applyFeedDialogs(
// not_null<Data::Feed*> feed,
// const MTPmessages_Dialogs &dialogs) {
// if (dialogs.type() == mtpc_messages_dialogsNotModified) {
// LOG(("API Error: "
// "messages.dialogsNotModified in ApiWrap::applyFeedDialogs."));
// return;
// }
// auto channels = std::vector<not_null<ChannelData*>>();
// dialogs.match([&](const MTPDmessages_dialogsNotModified &) {
// Unexpected("Type in ApiWrap::applyFeedDialogs.");
// }, [&](const auto &data) {
// _session->data().processUsers(data.vusers);
// _session->data().processChats(data.vchats);
// App::feedMsgs(data.vmessages.v, NewMessageLast);
// channels.reserve(data.vdialogs.v.size());
// for (const auto &dialog : data.vdialogs.v) {
// dialog.match([&](const MTPDdialog &data) {
// if (const auto peerId = peerFromMTP(data.vpeer)) {
// if (peerIsChannel(peerId)) { // #TODO archive
// const auto history = _session->data().history(peerId);
// history->applyDialog(data);
// channels.emplace_back(history->peer->asChannel());
// } else {
// LOG(("API Error: "
// "Unexpected peer in folder dialogs list."));
// }
// }
// }, [&](const MTPDdialogFolder &data) {
// LOG(("API Error: "
// "Unexpected dialogFolder in folder dialogs list."));
// });
// }
// });
//
// feed->setChannels(channels);
// _session->data().sendHistoryChangeNotifications();
//}
void ApiWrap::changeDialogUnreadMark(
not_null<History*> history,

View File

@ -64,7 +64,7 @@ public:
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
void savePinnedOrder();
void savePinnedOrder(FolderId folderId);
void toggleHistoryArchived(
not_null<History*> history,
bool archived,
@ -79,15 +79,13 @@ public:
QString exportDirectMessageLink(not_null<HistoryItem*> item);
void requestContacts();
void requestDialogs();
void requestFolderDialogs(FolderId folderId);
void requestPinnedDialogs();
void requestDialogs(FolderId folderId);
void requestPinnedDialogs(FolderId folderId);
void requestMoreBlockedByDateDialogs();
rpl::producer<bool> dialogsLoadMayBlockByDate() const;
rpl::producer<bool> dialogsLoadBlockedByDate() const;
void requestDialogEntry(not_null<Data::Folder*> folder);
//void requestFeedDialogsEntries(not_null<Data::Feed*> feed); // #feed
void requestDialogEntry(
not_null<History*> history,
Fn<void()> callback = nullptr);
@ -460,6 +458,10 @@ private:
MsgId offsetId = 0;
PeerData *offsetPeer = nullptr;
mtpRequestId requestId = 0;
bool listReceived = false;
mtpRequestId pinnedRequestId = 0;
bool pinnedReceived = false;
};
void setupSupportMode();
@ -485,9 +487,6 @@ private:
QVector<MTPInputMessage> collectMessageIds(const MessageDataRequests &requests);
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
void applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs);
//void applyFeedDialogs( // #feed
// not_null<Data::Feed*> feed,
// const MTPmessages_Dialogs &dialogs);
void gotChatFull(
not_null<PeerData*> peer,
@ -762,9 +761,6 @@ private:
rpl::variable<bool> _dialogsLoadMayBlockByDate = false;
rpl::variable<bool> _dialogsLoadBlockedByDate = false;
bool _pinnedDialogsReceived = false;
mtpRequestId _pinnedDialogsRequestId = 0;
base::flat_map<FolderId, DialogsLoadState> _foldersLoadState;
rpl::event_stream<SendOptions> _sendActions;

View File

@ -593,7 +593,7 @@ void Application::allKeysDestroyed() {
}
void Application::suggestMainDcId(MTP::DcId mainDcId) {
Assert(_mtproto != nullptr);
Expects(_mtproto != nullptr);
_mtproto->suggestMainDcId(mainDcId);
if (_private->mtpConfig.mainDcId != MTP::Instance::Config::kNotSetMainDc) {
@ -602,7 +602,7 @@ void Application::suggestMainDcId(MTP::DcId mainDcId) {
}
void Application::destroyStaleAuthorizationKeys() {
Assert(_mtproto != nullptr);
Expects(_mtproto != nullptr);
for (const auto &key : _mtproto->getKeysForWrite()) {
// Disable this for now.
@ -616,6 +616,14 @@ void Application::destroyStaleAuthorizationKeys() {
}
}
void Application::configUpdated() {
_configUpdates.fire({});
}
rpl::producer<> Application::configUpdates() const {
return _configUpdates.events();
}
void Application::resetAuthorizationKeys() {
_mtproto = nullptr;
startMtp();

View File

@ -140,6 +140,8 @@ public:
}
void suggestMainDcId(MTP::DcId mainDcId);
void destroyStaleAuthorizationKeys();
void configUpdated();
[[nodiscard]] rpl::producer<> configUpdates() const;
// Databases
Storage::Databases &databases() {
@ -276,6 +278,7 @@ private:
std::unique_ptr<MTP::DcOptions> _dcOptions;
std::unique_ptr<MTP::Instance> _mtproto;
std::unique_ptr<MTP::Instance> _mtprotoForKeysDestroy;
rpl::event_stream<> _configUpdates;
std::unique_ptr<AuthSession> _authSession;
base::Observable<void> _authSessionChanged;
base::Observable<void> _passcodedChanged;

View File

@ -42,6 +42,7 @@ Folder::Folder(not_null<Data::Session*> owner, FolderId id)
, _id(id)
, _chatsList(Dialogs::SortMode::Date)
, _importantChatsList(Dialogs::SortMode::Date)
, _pinnedChatsList(Global::PinnedDialogsInFolderMax())
, _name(lang(lng_archived_chats)) {
indexNameParts();
}
@ -234,6 +235,36 @@ void Folder::requestChatListMessage() {
}
}
void Folder::setPinnedChatsLimit(int limit) {
_pinnedChatsList.setLimit(limit);
}
void Folder::setChatPinned(const Dialogs::Key &key, bool pinned) {
_pinnedChatsList.setPinned(key, pinned);
}
void Folder::addPinnedChat(const Dialogs::Key &key) {
_pinnedChatsList.addPinned(key);
}
void Folder::applyPinnedChats(const QVector<MTPDialogPeer> &list) {
_pinnedChatsList.applyList(&owner(), list);
}
const std::vector<Dialogs::Key> &Folder::pinnedChatsOrder() const {
return _pinnedChatsList.order();
}
void Folder::clearPinnedChats() {
_pinnedChatsList.clear();
}
void Folder::reorderTwoPinnedChats(
const Dialogs::Key &key1,
const Dialogs::Key &key2) {
_pinnedChatsList.reorder(key1, key2);
}
TimeId Folder::adjustedChatListTimeId() const {
return _chatsList.empty()
? TimeId(0)
@ -286,10 +317,18 @@ void Folder::applyDialog(const MTPDdialogFolder &data) {
//}
if (_chatsList.size() < kLoadedChatsMinCount) {
session().api().requestFolderDialogs(_id);
session().api().requestDialogs(_id);
}
}
void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
const auto folderId = data.has_folder_id() ? data.vfolder_id.v : 0;
if (folderId != 0) {
LOG(("API Error: Nested folders detected."));
}
owner().setChatPinned(this, data.is_pinned());
}
void Folder::changedInChatListHook(Dialogs::Mode list, bool added) {
if (list != Dialogs::Mode::All) {
return;

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_entry.h"
#include "dialogs/dialogs_indexed_list.h"
#include "dialogs/dialogs_pinned_list.h"
#include "data/data_messages.h"
class ChannelData;
@ -45,6 +46,8 @@ public:
not_null<Dialogs::IndexedList*> chatsList(Dialogs::Mode list);
void applyDialog(const MTPDdialogFolder &data);
void applyPinnedUpdate(const MTPDupdateDialogPinned &data);
void setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount);
//void setUnreadPosition(const MessagePosition &position); // #feed
void unreadCountChanged(
@ -58,6 +61,22 @@ public:
//bool unreadMark() const;
//int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
void setPinnedChatsLimit(int limit);
// Places on the last place in the list otherwise.
// Does nothing if already pinned.
void addPinnedChat(const Dialogs::Key &key);
// if (pinned) places on the first place in the list.
void setChatPinned(const Dialogs::Key &key, bool pinned);
void applyPinnedChats(const QVector<MTPDialogPeer> &list);
const std::vector<Dialogs::Key> &pinnedChatsOrder() const;
void clearPinnedChats();
void reorderTwoPinnedChats(
const Dialogs::Key &key1,
const Dialogs::Key &key2);
TimeId adjustedChatListTimeId() const override;
int unreadCount() const;
bool unreadCountKnown() const;
@ -100,6 +119,7 @@ private:
FolderId _id = 0;
Dialogs::IndexedList _chatsList;
Dialogs::IndexedList _importantChatsList;
Dialogs::PinnedList _pinnedChatsList;
bool _chatsListLoaded = false;
QString _name;

View File

@ -154,6 +154,7 @@ Session::Session(not_null<AuthSession*> session)
, _importantChatsList(Dialogs::SortMode::Date)
, _contactsList(Dialogs::SortMode::Name)
, _contactsNoChatsList(Dialogs::SortMode::Name)
, _pinnedChatsList(Global::PinnedDialogsCountMax())
, _selfDestructTimer([=] { checkSelfDestructItems(); })
, _sendActionsAnimation([=](crl::time now) {
return sendActionsAnimationCallback(now);
@ -167,6 +168,14 @@ Session::Session(not_null<AuthSession*> session)
setupChannelLeavingViewer();
setupPeerNameViewer();
setupUserIsContactViewer();
Core::App().configUpdates(
) | rpl::start_with_next([=] {
_pinnedChatsList.setLimit(Global::PinnedDialogsCountMax());
for (const auto &[folderId, folder] : _folders) {
folder->setPinnedChatsLimit(Global::PinnedDialogsInFolderMax());
}
}, _lifetime);
}
void Session::clear() {
@ -731,7 +740,9 @@ History *Session::historyLoaded(const PeerData *peer) {
void Session::deleteConversationLocally(not_null<PeerData*> peer) {
const auto history = historyLoaded(peer);
if (history) {
setPinnedDialog(history, false);
if (history->folderKnown()) {
setChatPinned(history, false);
}
App::main()->removeDialog(history);
history->clear(peer->isChannel()
? History::ClearType::Unload
@ -1341,37 +1352,61 @@ MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const {
return { 1, item->fullId() };
}
void Session::setPinnedDialog(const Dialogs::Key &key, bool pinned) {
setIsPinned(key, pinned);
void Session::setChatPinned(const Dialogs::Key &key, bool pinned) {
Expects(key.entry()->folderKnown());
if (const auto folder = key.entry()->folder()) {
folder->setChatPinned(key, pinned);
} else {
_pinnedChatsList.setPinned(key, pinned);
}
}
void Session::applyPinnedDialogs(const QVector<MTPDialogPeer> &list) {
clearPinnedDialogs();
for (auto i = list.size(); i != 0;) {
list[--i].match([&](const MTPDdialogPeer &data) {
if (const auto peerId = peerFromMTP(data.vpeer)) {
setPinnedDialog(history(peerId), true);
void Session::setPinnedFromDialog(const Dialogs::Key &key, bool pinned) {
Expects(key.entry()->folderKnown());
if (const auto folder = key.entry()->folder()) {
if (pinned) {
folder->addPinnedChat(key);
} else {
folder->setChatPinned(key, false);
}
} else if (pinned) {
_pinnedChatsList.addPinned(key);
} else {
_pinnedChatsList.setPinned(key, false);
}
}
void Session::applyPinnedChats(
FolderId folderId,
const QVector<MTPDialogPeer> &list) {
const auto folder = folderId ? this->folder(folderId).get() : nullptr;
for (const auto &peer : list) {
peer.match([&](const MTPDdialogPeer &data) {
const auto history = this->history(peerFromMTP(data.vpeer));
if (folder) {
history->setFolder(folder);
} else {
history->clearFolder();
}
}, [&](const MTPDdialogPeerFolder &data) {
const auto folderId = data.vfolder_id.v;
setPinnedDialog(folder(folderId), true);
if (folder) {
LOG(("API Error: Nested folders detected."));
}
});
}
if (folder) {
folder->applyPinnedChats(list);
} else {
_pinnedChatsList.applyList(this, list);
}
}
void Session::applyDialogs(
FolderId requestFolderId,
const QVector<MTPMessage> &messages,
const QVector<MTPDialog> &dialogs) {
for (const auto &dialog : dialogs | ranges::view::reverse) {
dialog.match([&](const MTPDdialog &data) {
if (const auto peer = peerFromMTP(data.vpeer)) {
setPinnedDialog(history(peer), data.is_pinned());
}
}, [&](const MTPDdialogFolder &data) {
setPinnedDialog(processFolder(data.vfolder), data.is_pinned());
});
}
App::feedMsgs(messages, NewMessageLast);
for (const auto &dialog : dialogs) {
dialog.match([&](const auto &data) {
@ -1388,6 +1423,7 @@ void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) {
const auto history = session().data().history(peerId);
history->applyDialog(requestFolderId, data);
setPinnedFromDialog(history, data.is_pinned());
if (!history->fixedOnTopIndex() && !history->isPinnedDialog()) {
const auto date = history->chatListTimeId();
@ -1408,12 +1444,13 @@ void Session::applyDialog(FolderId requestFolderId, const MTPDdialog &data) {
void Session::applyDialog(
FolderId requestFolderId,
const MTPDdialogFolder &dialog) {
const MTPDdialogFolder &data) {
if (requestFolderId != 0) {
LOG(("API Error: requestFolderId != 0 for dialogFolder."));
}
const auto folder = processFolder(dialog.vfolder);
folder->applyDialog(dialog);
const auto folder = processFolder(data.vfolder);
folder->applyDialog(data);
setPinnedFromDialog(folder, data.is_pinned());
if (!folder->fixedOnTopIndex() && !folder->isPinnedDialog()) {
const auto date = folder->chatListTimeId();
@ -1441,69 +1478,49 @@ void Session::addAllSavedPeers() {
addSavedPeersAfter(QDateTime());
}
int Session::pinnedDialogsCount() const {
return _pinnedDialogs.size();
int Session::pinnedChatsCount(FolderId folderId) const {
return pinnedChatsOrder(folderId).size();
}
const std::deque<Dialogs::Key> &Session::pinnedDialogsOrder() const {
return _pinnedDialogs;
int Session::pinnedChatsLimit(FolderId folderId) const {
return folderId
? Global::PinnedDialogsInFolderMax()
: Global::PinnedDialogsCountMax();
}
void Session::clearPinnedDialogs() {
while (!_pinnedDialogs.empty()) {
setPinnedDialog(_pinnedDialogs.back(), false);
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
FolderId folderId) const {
if (folderId) {
if (const auto folder = folderLoaded(folderId)) {
return folder->pinnedChatsOrder();
}
static const auto result = std::vector<Dialogs::Key>();
return result;
}
return _pinnedChatsList.order();
}
void Session::clearPinnedChats(FolderId folderId) {
if (folderId) {
if (const auto folder = folderLoaded(folderId)) {
folder->clearPinnedChats();
}
} else {
_pinnedChatsList.clear();
}
}
void Session::reorderTwoPinnedDialogs(
void Session::reorderTwoPinnedChats(
const Dialogs::Key &key1,
const Dialogs::Key &key2) {
const auto &order = pinnedDialogsOrder();
const auto index1 = ranges::find(order, key1) - begin(order);
const auto index2 = ranges::find(order, key2) - begin(order);
Assert(index1 >= 0 && index1 < order.size());
Assert(index2 >= 0 && index2 < order.size());
Assert(index1 != index2);
std::swap(_pinnedDialogs[index1], _pinnedDialogs[index2]);
key1.entry()->cachePinnedIndex(index2 + 1);
key2.entry()->cachePinnedIndex(index1 + 1);
}
Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown());
Expects(key1.entry()->folder() == key2.entry()->folder());
void Session::setIsPinned(const Dialogs::Key &key, bool pinned) {
const auto already = ranges::find(_pinnedDialogs, key);
if (pinned) {
if (already != end(_pinnedDialogs)) {
auto saved = std::move(*already);
const auto alreadyIndex = already - end(_pinnedDialogs);
const auto count = int(size(_pinnedDialogs));
Assert(alreadyIndex < count);
for (auto index = alreadyIndex + 1; index != count; ++index) {
_pinnedDialogs[index - 1] = std::move(_pinnedDialogs[index]);
_pinnedDialogs[index - 1].entry()->cachePinnedIndex(index);
}
_pinnedDialogs.back() = std::move(saved);
_pinnedDialogs.back().entry()->cachePinnedIndex(count);
} else {
_pinnedDialogs.push_back(key);
if (_pinnedDialogs.size() > Global::PinnedDialogsCountMax()) {
_pinnedDialogs.front().entry()->cachePinnedIndex(0);
_pinnedDialogs.pop_front();
auto index = 0;
for (const auto &pinned : _pinnedDialogs) {
pinned.entry()->cachePinnedIndex(++index);
}
} else {
key.entry()->cachePinnedIndex(_pinnedDialogs.size());
}
}
} else if (!pinned && already != end(_pinnedDialogs)) {
key.entry()->cachePinnedIndex(0);
_pinnedDialogs.erase(already);
auto index = 0;
for (const auto &pinned : _pinnedDialogs) {
pinned.entry()->cachePinnedIndex(++index);
}
const auto folder = key1.entry()->folder();
if (folder) {
folder->reorderTwoPinnedChats(key1, key2);
} else {
_pinnedChatsList.reorder(key1, key2);
}
}
@ -2937,7 +2954,7 @@ not_null<Folder*> Session::folder(FolderId id) {
return it->second.get();
}
Folder *Session::folderLoaded(FolderId id) {
Folder *Session::folderLoaded(FolderId id) const {
const auto it = _folders.find(id);
return (it == end(_folders)) ? nullptr : it->second.get();
}

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/stickers.h"
#include "dialogs/dialogs_key.h"
#include "dialogs/dialogs_indexed_list.h"
#include "dialogs/dialogs_pinned_list.h"
#include "data/data_groups.h"
#include "data/data_notify_settings.h"
#include "history/history_location_manager.h"
@ -304,12 +305,16 @@ public:
void addSavedPeersAfter(const QDateTime &date);
void addAllSavedPeers();
int pinnedDialogsCount() const;
const std::deque<Dialogs::Key> &pinnedDialogsOrder() const;
void setPinnedDialog(const Dialogs::Key &key, bool pinned);
void clearPinnedDialogs();
void applyPinnedDialogs(const QVector<MTPDialogPeer> &list);
void reorderTwoPinnedDialogs(
int pinnedChatsCount(FolderId folderId) const;
int pinnedChatsLimit(FolderId folderId) const;
const std::vector<Dialogs::Key> &pinnedChatsOrder(
FolderId folderId) const;
void setChatPinned(const Dialogs::Key &key, bool pinned);
void clearPinnedChats(FolderId folderId);
void applyPinnedChats(
FolderId folderId,
const QVector<MTPDialogPeer> &list);
void reorderTwoPinnedChats(
const Dialogs::Key &key1,
const Dialogs::Key &key2);
@ -513,7 +518,7 @@ public:
void unregisterItemView(not_null<ViewElement*> view);
[[nodiscard]] not_null<Folder*> folder(FolderId id);
[[nodiscard]] Folder *folderLoaded(FolderId id);
[[nodiscard]] Folder *folderLoaded(FolderId id) const;
not_null<Folder*> processFolder(const MTPFolder &data);
not_null<Folder*> processFolder(const MTPDfolder &data);
//void setDefaultFeedId(FeedId id); // #feed
@ -694,7 +699,7 @@ private:
}
void userIsContactUpdated(not_null<UserData*> user);
void setIsPinned(const Dialogs::Key &key, bool pinned);
void setPinnedFromDialog(const Dialogs::Key &key, bool pinned);
NotifySettings &defaultNotifySettings(not_null<const PeerData*> peer);
const NotifySettings &defaultNotifySettings(
@ -777,6 +782,7 @@ private:
Dialogs::IndexedList _importantChatsList;
Dialogs::IndexedList _contactsList;
Dialogs::IndexedList _contactsNoChatsList;
Dialogs::PinnedList _pinnedChatsList;
base::Timer _selfDestructTimer;
std::vector<FullMsgId> _selfDestructItems;
@ -835,7 +841,6 @@ private:
base::flat_set<not_null<GameData*>> _gamesUpdated;
base::flat_set<not_null<PollData*>> _pollsUpdated;
std::deque<Dialogs::Key> _pinnedDialogs;
base::flat_map<FolderId, std::unique_ptr<Folder>> _folders;
//rpl::variable<FeedId> _defaultFeedId = FeedId(); // #feed
Groups _groups;

View File

@ -34,7 +34,7 @@ uint64 FixedOnTopDialogPos(int index) {
}
uint64 PinnedDialogPos(int pinnedIndex) {
return 0xFFFFFFFF00000000ULL + pinnedIndex;
return 0xFFFFFFFF000000FFULL - pinnedIndex;
}
} // namespace

View File

@ -81,7 +81,7 @@ void IndexedList::movePinned(Row *row, int deltaSign) {
Assert(swapPinnedIndexWith != cbegin());
--swapPinnedIndexWith;
}
Auth().data().reorderTwoPinnedDialogs(
Auth().data().reorderTwoPinnedChats(
row->key(),
(*swapPinnedIndexWith)->key());
}

View File

@ -275,13 +275,28 @@ int DialogsInner::openedFolderSkip() const {
return st::dialogsSearchInHeight;
}
bool DialogsInner::openFolder(not_null<Data::Folder*> folder) {
return changeOpenedFolder(folder);
}
bool DialogsInner::cancelFolder() {
if (!_openedFolder) {
return changeOpenedFolder(nullptr);
}
bool DialogsInner::changeOpenedFolder(Data::Folder *folder) {
if (_openedFolder == folder) {
return false;
}
stopReorderPinned();
clearSelection();
_openedFolder = nullptr;
_closeOpenedFolder->hide();
_openedFolder = folder;
_closeOpenedFolder->setVisible(folder != nullptr);
if (folder) {
_openedFolderText.setText(
st::msgNameStyle,
folder->chatListName(),
Ui::DialogTextOptions());
}
refresh();
return true;
}
@ -920,18 +935,25 @@ void DialogsInner::mousePressEvent(QMouseEvent *e) {
}
void DialogsInner::checkReorderPinnedStart(QPoint localPosition) {
if (_pressed != nullptr && !_dragging && _state == State::Default) {
if (qAbs(localPosition.y() - _dragStart.y()) >= ConvertScale(kStartReorderThreshold)) {
_dragging = _pressed;
if (updateReorderIndexGetCount() < 2) {
_dragging = nullptr;
} else {
_pinnedOrder = session().data().pinnedDialogsOrder();
_pinnedRows[_draggingIndex].yadd = anim::value(0, localPosition.y() - _dragStart.y());
_pinnedRows[_draggingIndex].animStartTime = crl::now();
_pinnedShiftAnimation.start();
}
}
if (!_pressed || _dragging || _state != State::Default) {
return;
} else if (qAbs(localPosition.y() - _dragStart.y())
< ConvertScale(kStartReorderThreshold)) {
return;
}
_dragging = _pressed;
if (updateReorderIndexGetCount() < 2) {
_dragging = nullptr;
} else {
const auto folderId = _openedFolder ? _openedFolder->id() : 0;
const auto &order = session().data().pinnedChatsOrder(folderId);
_pinnedOnDragStart = base::flat_set<Dialogs::Key>{
order.begin(),
order.end()
};
_pinnedRows[_draggingIndex].yadd = anim::value(0, localPosition.y() - _dragStart.y());
_pinnedRows[_draggingIndex].animStartTime = crl::now();
_pinnedShiftAnimation.start();
}
}
@ -967,16 +989,17 @@ int DialogsInner::countPinnedIndex(Dialogs::Row *ofRow) {
}
void DialogsInner::savePinnedOrder() {
const auto &newOrder = session().data().pinnedDialogsOrder();
if (newOrder.size() != _pinnedOrder.size()) {
const auto folderId = _openedFolder ? _openedFolder->id() : 0;
const auto &newOrder = session().data().pinnedChatsOrder(folderId);
if (newOrder.size() != _pinnedOnDragStart.size()) {
return; // Something has changed in the set of pinned chats.
}
for (const auto &pinned : newOrder) {
if (!base::contains(_pinnedOrder, pinned)) {
for (const auto &key : newOrder) {
if (!_pinnedOnDragStart.contains(key)) {
return; // Something has changed in the set of pinned chats.
}
}
session().api().savePinnedOrder();
session().api().savePinnedOrder(folderId);
}
void DialogsInner::finishReorderPinned() {
@ -1769,7 +1792,7 @@ void DialogsInner::clearSearchResults(bool clearPeerSearchResults) {
_searchResults.clear();
_searchedCount = _searchedMigratedCount = 0;
_lastSearchDate = 0;
_lastSearchPeer = 0;
_lastSearchPeer = nullptr;
_lastSearchId = _lastSearchMigratedId = 0;
}
@ -2457,14 +2480,7 @@ bool DialogsInner::chooseRow() {
? ShowAtUnreadMsgId
: chosen.message.fullId.msg));
} else if (const auto folder = chosen.key.folder()) {
clearSelection();
_openedFolder = folder;
_closeOpenedFolder->show();
_openedFolderText.setText(
st::msgNameStyle,
folder->chatListName(),
Ui::DialogTextOptions());
refresh();
openFolder(folder);
}
if (openSearchResult && !session().supportMode()) {
emit clearSearchQuery();

View File

@ -50,6 +50,7 @@ public:
void activate();
bool openFolder(not_null<Data::Folder*> folder);
bool cancelFolder();
void selectSkip(int32 direction);
void selectSkipPage(int32 pixels, int32 direction);
@ -273,6 +274,8 @@ private:
bool pinnedShiftAnimationCallback(crl::time now);
void handleChatMigration(not_null<ChatData*> chat);
bool changeOpenedFolder(Data::Folder *folder);
not_null<Window::Controller*> _controller;
bool _mouseSelection = false;
@ -298,7 +301,7 @@ private:
};
std::vector<PinnedRow> _pinnedRows;
Ui::Animations::Basic _pinnedShiftAnimation;
std::deque<Dialogs::Key> _pinnedOrder;
base::flat_set<Dialogs::Key> _pinnedOnDragStart;
// Remember the last currently dragged row top shift for updating area.
int _aboveTopShift = -1;

View File

@ -0,0 +1,109 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "dialogs/dialogs_pinned_list.h"
#include "dialogs/dialogs_key.h"
#include "dialogs/dialogs_entry.h"
#include "data/data_session.h"
namespace Dialogs {
PinnedList::PinnedList(int limit) : _limit(limit) {
Expects(limit > 0);
}
void PinnedList::setLimit(int limit) {
Expects(limit > 0);
if (_limit == limit) {
return;
}
_limit = limit;
applyLimit(_limit);
}
void PinnedList::addPinned(const Key &key) {
Expects(key.entry()->folderKnown());
addPinnedGetPosition(key);
}
int PinnedList::addPinnedGetPosition(const Key &key) {
const auto already = ranges::find(_data, key);
if (already != end(_data)) {
return already - begin(_data);
}
applyLimit(_limit - 1);
const auto position = int(_data.size());
_data.push_back(key);
key.entry()->cachePinnedIndex(position);
return position;
}
void PinnedList::setPinned(const Key &key, bool pinned) {
Expects(key.entry()->folderKnown());
if (pinned) {
const int position = addPinnedGetPosition(key);
if (position) {
const auto begin = _data.begin();
std::rotate(begin, begin + position, begin + position + 1);
for (auto i = 0; i != position + 1; ++i) {
_data[i].entry()->cachePinnedIndex(i + 1);
}
}
} else if (const auto it = ranges::find(_data, key); it != end(_data)) {
const auto index = (it - begin(_data));
_data.erase(it);
key.entry()->cachePinnedIndex(0);
for (auto i = index, count = int(size(_data)); i != count; ++i) {
_data[i].entry()->cachePinnedIndex(i + 1);
}
}
}
void PinnedList::applyLimit(int limit) {
Expects(limit >= 0);
while (_data.size() > limit) {
setPinned(_data.back(), false);
}
}
void PinnedList::clear() {
applyLimit(0);
}
void PinnedList::applyList(
not_null<Data::Session*> owner,
const QVector<MTPDialogPeer> &list) {
clear();
for (const auto &peer : ranges::view::reverse(list)) {
peer.match([&](const MTPDdialogPeer &data) {
if (const auto peerId = peerFromMTP(data.vpeer)) {
setPinned(owner->history(peerId), true);
}
}, [&](const MTPDdialogPeerFolder &data) {
const auto folderId = data.vfolder_id.v;
setPinned(owner->folder(folderId), true);
});
}
}
void PinnedList::reorder(const Key &key1, const Key &key2) {
const auto index1 = ranges::find(_data, key1) - begin(_data);
const auto index2 = ranges::find(_data, key2) - begin(_data);
Assert(index1 >= 0 && index1 < _data.size());
Assert(index2 >= 0 && index2 < _data.size());
Assert(index1 != index2);
std::swap(_data[index1], _data[index2]);
key1.entry()->cachePinnedIndex(index2 + 1);
key2.entry()->cachePinnedIndex(index1 + 1);
}
} // namespace Dialogs

View File

@ -0,0 +1,51 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Data {
class Session;
} // namespace Data
namespace Dialogs {
class Key;
class PinnedList {
public:
explicit PinnedList(int limit);
void setLimit(int limit);
// Places on the last place in the list otherwise.
// Does nothing if already pinned.
void addPinned(const Key &key);
// if (pinned) places on the first place in the list.
void setPinned(const Key &key, bool pinned);
void clear();
void applyList(
not_null<Data::Session*> owner,
const QVector<MTPDialogPeer> &list);
void reorder(const Key &key1, const Key &key2);
const std::vector<Key> &order() const {
return _data;
}
private:
int addPinnedGetPosition(const Key &key);
void applyLimit(int limit);
int _limit = 0;
std::vector<Key> _data;
};
} // namespace Dialogs

View File

@ -247,7 +247,11 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
&& !_searchFullMigrated))) {
onSearchMore();
} else {
session().api().requestDialogs();
const auto folder = _inner->shownFolder();
const auto folderId = folder ? folder->id() : FolderId(0);
if (!folderId || !folder->chatsListLoaded()) {
session().api().requestDialogs(folderId);
}
}
});
_inner->listBottomReached(
@ -1063,14 +1067,6 @@ void DialogsWidget::onListScroll() {
// Fix button rendering glitch, Qt bug with WA_OpaquePaintEvent widgets.
_scrollToTop->update();
if (const auto folder = _inner->shownFolder()) {
if (!folder->chatsListLoaded()
&& (scrollTop + height() * PreloadHeightsCount
>= _scroll->scrollTopMax())) {
session().api().requestFolderDialogs(folder->id());
}
}
}
void DialogsWidget::applyFilterUpdate(bool force) {

View File

@ -391,6 +391,7 @@ struct Data {
int32 StickersRecentLimit = 30;
int32 StickersFavedLimit = 5;
int32 PinnedDialogsCountMax = 5;
int32 PinnedDialogsInFolderMax = 100;
QString InternalLinksDomain = qsl("https://t.me/");
int32 ChannelsReadMediaPeriod = 86400 * 7;
int32 CallReceiveTimeoutMs = 20000;
@ -525,6 +526,7 @@ DefineVar(Global, bool, RevokePrivateInbox);
DefineVar(Global, int32, StickersRecentLimit);
DefineVar(Global, int32, StickersFavedLimit);
DefineVar(Global, int32, PinnedDialogsCountMax);
DefineVar(Global, int32, PinnedDialogsInFolderMax);
DefineVar(Global, QString, InternalLinksDomain);
DefineVar(Global, int32, ChannelsReadMediaPeriod);
DefineVar(Global, int32, CallReceiveTimeoutMs);

View File

@ -248,6 +248,7 @@ DeclareVar(bool, RevokePrivateInbox);
DeclareVar(int32, StickersRecentLimit);
DeclareVar(int32, StickersFavedLimit);
DeclareVar(int32, PinnedDialogsCountMax);
DeclareVar(int32, PinnedDialogsInFolderMax);
DeclareVar(QString, InternalLinksDomain);
DeclareVar(int32, ChannelsReadMediaPeriod);
DeclareVar(int32, CallReceiveTimeoutMs);

View File

@ -1835,6 +1835,9 @@ void History::setFolderPointer(Data::Folder *folder) {
if (_folder == folder) {
return;
}
if (isPinnedDialog()) {
owner().setChatPinned(this, false);
}
const auto wasKnown = folderKnown();
const auto wasInAll = inChatList(Mode::All);
const auto wasInImportant = wasInAll && inChatList(Mode::Important);
@ -1864,6 +1867,18 @@ void History::setFolderPointer(Data::Folder *folder) {
}
}
void History::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
const auto folderId = data.has_folder_id() ? data.vfolder_id.v : 0;
if (!folderKnown()) {
if (folderId) {
setFolder(owner().folder(folderId));
} else {
clearFolder();
}
}
owner().setChatPinned(this, data.is_pinned());
}
TimeId History::adjustedChatListTimeId() const {
const auto result = chatListTimeId();
if (const auto draft = cloudDraft()) {

View File

@ -192,6 +192,7 @@ public:
void unknownMessageDeleted(MsgId messageId);
void applyDialogTopMessage(MsgId topMessageId);
void applyDialog(FolderId requestFolderId, const MTPDdialog &data);
void applyPinnedUpdate(const MTPDupdateDialogPinned &data);
void applyDialogFields(
int unreadCount,
MsgId maxInboxRead,

View File

@ -2931,7 +2931,7 @@ void MainWidget::gotState(const MTPupdates_State &state) {
_noUpdatesTimer.callOnce(kNoUpdatesTimeout);
_ptsWaiter.setRequesting(false);
session().api().requestDialogs();
session().api().requestDialogs(FolderId(0));
updateOnline();
}
@ -4269,7 +4269,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
case mtpc_updatePinnedDialogs: {
const auto &d = update.c_updatePinnedDialogs();
if (d.has_order()) {
const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0;
const auto done = [&] {
if (!d.has_order()) {
return false;
}
const auto &order = d.vorder.v;
const auto notLoaded = [&](const MTPDialogPeer &peer) {
return peer.match([&](const MTPDdialogPeer &data) {
@ -4281,41 +4285,52 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
};
const auto allLoaded = ranges::find_if(order, notLoaded)
== order.end();
if (allLoaded) {
session().data().applyPinnedDialogs(order);
} else {
session().api().requestPinnedDialogs();
if (!allLoaded) {
return false;
}
} else {
session().api().requestPinnedDialogs();
session().data().applyPinnedChats(folderId, order);
return true;
}();
if (!done) {
session().api().requestPinnedDialogs(folderId);
}
} break;
case mtpc_updateDialogPinned: {
const auto &d = update.c_updateDialogPinned();
d.vpeer.match([&](const MTPDdialogPeer &data) {
const auto folderId = d.has_folder_id() ? d.vfolder_id.v : 0;
const auto done = d.vpeer.match([&](const MTPDdialogPeer &data) {
const auto id = peerFromMTP(data.vpeer);
if (const auto history = session().data().historyLoaded(id)) {
session().data().setPinnedDialog(history, d.is_pinned());
} else {
DEBUG_LOG(("API Error: "
"pinned chat not loaded for peer %1"
).arg(id
));
session().api().requestPinnedDialogs();
history->applyPinnedUpdate(d);
return true;
}
DEBUG_LOG(("API Error: "
"pinned chat not loaded for peer %1, folder: %2"
).arg(id
).arg(folderId
));
return false;
}, [&](const MTPDdialogPeerFolder &data) {
if (folderId != 0) {
DEBUG_LOG(("API Error: Nested folders updateDialogPinned."));
return false;
}
const auto id = data.vfolder_id.v;
if (const auto folder = session().data().folderLoaded(id)) {
session().data().setPinnedDialog(folder, d.is_pinned());
} else {
DEBUG_LOG(("API Error: "
"pinned folder not loaded for folderId %1"
).arg(id
));
session().api().requestPinnedDialogs();
folder->applyPinnedUpdate(d);
return true;
}
DEBUG_LOG(("API Error: "
"pinned folder not loaded for folderId %1, folder: %2"
).arg(id
).arg(folderId
));
return false;
});
if (!done) {
session().api().requestPinnedDialogs(folderId);
}
} break;
case mtpc_updateChannel: {

View File

@ -773,7 +773,10 @@ void Instance::Private::configLoadDone(const MTPConfig &result) {
Global::SetRevokePrivateInbox(data.is_revoke_pm_inbox());
Global::SetStickersRecentLimit(data.vstickers_recent_limit.v);
Global::SetStickersFavedLimit(data.vstickers_faved_limit.v);
Global::SetPinnedDialogsCountMax(data.vpinned_dialogs_count_max.v);
Global::SetPinnedDialogsCountMax(
std::max(data.vpinned_dialogs_count_max.v, 1));
Global::SetPinnedDialogsInFolderMax(
std::max(data.vpinned_infolder_count_max.v, 1));
Core::App().setInternalLinkDomain(qs(data.vme_url_prefix));
Global::SetChannelsReadMediaPeriod(data.vchannels_read_media_period.v);
Global::SetWebFileDcId(data.vwebfile_dc_id.v);
@ -801,6 +804,8 @@ void Instance::Private::configLoadDone(const MTPConfig &result) {
? data.vbase_lang_pack_version.v
: 0));
Core::App().configUpdated();
if (data.has_autoupdate_url_prefix()) {
Local::writeAutoupdatePrefix(qs(data.vautoupdate_url_prefix));
}

View File

@ -80,12 +80,12 @@ public:
void fill();
private:
bool showInfo();
void addPinToggle();
void addInfo();
void addSearch();
void addNotifications();
void addUngroup();
//bool showInfo();
//void addPinToggle();
//void addInfo();
//void addSearch();
//void addNotifications();
//void addUngroup();
not_null<Controller*> _controller;
not_null<Data::Folder*> _folder;
@ -94,8 +94,8 @@ private:
};
History *FindWastedPin() {
const auto &order = Auth().data().pinnedDialogsOrder();
History *FindWastedPin(FolderId folderId) {
const auto &order = Auth().data().pinnedChatsOrder(folderId);
for (const auto &pinned : order) {
if (const auto history = pinned.history()) {
if (history->peer->isChat()
@ -113,16 +113,20 @@ void AddChatMembers(not_null<ChatData*> chat) {
}
bool PinnedLimitReached(Dialogs::Key key) {
const auto pinnedCount = Auth().data().pinnedDialogsCount();
const auto pinnedMax = Global::PinnedDialogsCountMax();
Expects(key.entry()->folderKnown());
const auto folder = key.entry()->folder();
const auto folderId = folder ? folder->id() : 0;
const auto pinnedCount = Auth().data().pinnedChatsCount(folderId);
const auto pinnedMax = Auth().data().pinnedChatsLimit(folderId);
if (pinnedCount < pinnedMax) {
return false;
}
// Some old chat, that was converted, maybe is still pinned.
if (const auto wasted = FindWastedPin()) {
Auth().data().setPinnedDialog(wasted, false);
Auth().data().setPinnedDialog(key, true);
Auth().api().savePinnedOrder();
if (const auto wasted = FindWastedPin(folderId)) {
Auth().data().setChatPinned(wasted, false);
Auth().data().setChatPinned(key, true);
Auth().api().savePinnedOrder(folderId);
} else {
auto errorText = lng_error_pinned_max(
lt_count,
@ -133,32 +137,34 @@ bool PinnedLimitReached(Dialogs::Key key) {
}
void TogglePinnedDialog(Dialogs::Key key) {
if (!key.entry()->folderKnown()) {
return;
}
const auto isPinned = !key.entry()->isPinnedDialog();
if (isPinned && PinnedLimitReached(key)) {
return;
}
Auth().data().setPinnedDialog(key, isPinned);
auto flags = MTPmessages_ToggleDialogPin::Flags(0);
if (isPinned) {
flags |= MTPmessages_ToggleDialogPin::Flag::f_pinned;
}
//MTP::send(MTPmessages_ToggleDialogPin( // #feed
// MTP_flags(flags),
// key.history()
// ? MTP_inputDialogPeer(key.history()->peer->input)
// : MTP_inputDialogPeerFeed(MTP_int(key.feed()->id()))));
if (key.history()) {
MTP::send(MTPmessages_ToggleDialogPin(
Auth().data().setChatPinned(key, isPinned);
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)));
MTP_inputDialogPeer(key.history()->peer->input)
)).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(

View File

@ -228,6 +228,8 @@
<(src_loc)/dialogs/dialogs_layout.h
<(src_loc)/dialogs/dialogs_list.cpp
<(src_loc)/dialogs/dialogs_list.h
<(src_loc)/dialogs/dialogs_pinned_list.cpp
<(src_loc)/dialogs/dialogs_pinned_list.h
<(src_loc)/dialogs/dialogs_row.cpp
<(src_loc)/dialogs/dialogs_row.h
<(src_loc)/dialogs/dialogs_search_from_controllers.cpp