mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-24 09:16:57 +00:00
Support pinned chats in folders.
This commit is contained in:
parent
607655941d
commit
58519300ea
Telegram
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -34,7 +34,7 @@ uint64 FixedOnTopDialogPos(int index) {
|
||||
}
|
||||
|
||||
uint64 PinnedDialogPos(int pinnedIndex) {
|
||||
return 0xFFFFFFFF00000000ULL + pinnedIndex;
|
||||
return 0xFFFFFFFF000000FFULL - pinnedIndex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
109
Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp
Normal file
109
Telegram/SourceFiles/dialogs/dialogs_pinned_list.cpp
Normal 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
|
51
Telegram/SourceFiles/dialogs/dialogs_pinned_list.h
Normal file
51
Telegram/SourceFiles/dialogs/dialogs_pinned_list.h
Normal 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
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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()) {
|
||||
|
@ -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,
|
||||
|
@ -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: {
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user