2018-01-05 15:57:18 +00:00
|
|
|
/*
|
|
|
|
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_entry.h"
|
|
|
|
|
|
|
|
#include "dialogs/dialogs_key.h"
|
|
|
|
#include "dialogs/dialogs_indexed_list.h"
|
2020-06-12 14:09:04 +00:00
|
|
|
#include "data/data_changes.h"
|
2019-04-16 14:05:56 +00:00
|
|
|
#include "data/data_session.h"
|
2019-04-17 13:22:37 +00:00
|
|
|
#include "data/data_folder.h"
|
2020-03-09 11:17:56 +00:00
|
|
|
#include "data/data_chat_filters.h"
|
2018-01-05 15:57:18 +00:00
|
|
|
#include "mainwidget.h"
|
2019-07-24 11:45:24 +00:00
|
|
|
#include "main/main_session.h"
|
2020-06-18 12:47:09 +00:00
|
|
|
#include "main/main_session_settings.h"
|
2018-01-11 19:33:26 +00:00
|
|
|
#include "history/history_item.h"
|
2018-01-13 12:45:11 +00:00
|
|
|
#include "history/history.h"
|
2019-09-13 06:06:02 +00:00
|
|
|
#include "app.h"
|
2019-04-16 14:05:56 +00:00
|
|
|
#include "styles/style_dialogs.h" // st::dialogsTextWidthMin
|
2018-01-05 15:57:18 +00:00
|
|
|
|
|
|
|
namespace Dialogs {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
auto DialogsPosToTopShift = 0;
|
|
|
|
|
2018-07-31 14:34:14 +00:00
|
|
|
uint64 DialogPosFromDate(TimeId date) {
|
|
|
|
if (!date) {
|
2018-01-05 15:57:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-07-31 14:34:14 +00:00
|
|
|
return (uint64(date) << 32) | (++DialogsPosToTopShift);
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2019-04-18 11:31:30 +00:00
|
|
|
uint64 FixedOnTopDialogPos(int index) {
|
|
|
|
return 0xFFFFFFFFFFFF000FULL - index;
|
2018-05-11 14:03:53 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 15:57:18 +00:00
|
|
|
uint64 PinnedDialogPos(int pinnedIndex) {
|
2019-04-19 08:47:49 +00:00
|
|
|
return 0xFFFFFFFF000000FFULL - pinnedIndex;
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2020-06-12 14:09:04 +00:00
|
|
|
Entry::Entry(not_null<Data::Session*> owner, Type type)
|
2018-01-05 15:57:18 +00:00
|
|
|
: lastItemTextCache(st::dialogsTextWidthMin)
|
2019-04-16 14:05:56 +00:00
|
|
|
, _owner(owner)
|
2020-06-12 14:09:04 +00:00
|
|
|
, _isFolder(type == Type::Folder) {
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 14:05:56 +00:00
|
|
|
Data::Session &Entry::owner() const {
|
|
|
|
return *_owner;
|
|
|
|
}
|
|
|
|
|
2019-07-24 11:45:24 +00:00
|
|
|
Main::Session &Entry::session() const {
|
2019-04-16 14:05:56 +00:00
|
|
|
return _owner->session();
|
|
|
|
}
|
|
|
|
|
2020-06-12 14:09:04 +00:00
|
|
|
History *Entry::asHistory() {
|
|
|
|
return _isFolder ? nullptr : static_cast<History*>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Data::Folder *Entry::asFolder() {
|
|
|
|
return _isFolder ? static_cast<Data::Folder*>(this) : nullptr;
|
|
|
|
}
|
|
|
|
|
2020-03-17 13:04:30 +00:00
|
|
|
void Entry::pinnedIndexChanged(int was, int now) {
|
|
|
|
if (session().supportMode()) {
|
|
|
|
// Force reorder in support mode.
|
|
|
|
_sortKeyInChatList = 0;
|
|
|
|
}
|
|
|
|
updateChatListSortPosition();
|
|
|
|
updateChatListEntry();
|
|
|
|
if ((was != 0) != (now != 0)) {
|
|
|
|
changedChatListPinHook();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::cachePinnedIndex(FilterId filterId, int index) {
|
|
|
|
const auto i = _pinnedIndex.find(filterId);
|
|
|
|
const auto was = (i != end(_pinnedIndex)) ? i->second : 0;
|
|
|
|
if (index == was) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!index) {
|
|
|
|
_pinnedIndex.erase(i);
|
|
|
|
pinnedIndexChanged(was, index);
|
|
|
|
} else {
|
|
|
|
if (!was) {
|
|
|
|
_pinnedIndex.emplace(filterId, index);
|
|
|
|
} else {
|
|
|
|
i->second = index;
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
2020-03-17 13:04:30 +00:00
|
|
|
pinnedIndexChanged(was, index);
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-29 08:46:02 +00:00
|
|
|
void Entry::cacheTopPromoted(bool promoted) {
|
|
|
|
if (_isTopPromoted == promoted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_isTopPromoted = promoted;
|
|
|
|
updateChatListSortPosition();
|
|
|
|
updateChatListEntry();
|
|
|
|
if (!_isTopPromoted) {
|
|
|
|
updateChatListExistence();
|
2018-05-11 14:03:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-24 12:40:20 +00:00
|
|
|
bool Entry::isTopPromoted() const {
|
|
|
|
return _isTopPromoted;
|
|
|
|
}
|
|
|
|
|
2018-01-23 16:51:12 +00:00
|
|
|
bool Entry::needUpdateInChatList() const {
|
2019-04-22 14:22:39 +00:00
|
|
|
return inChatList() || shouldBeInChatList();
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 15:57:18 +00:00
|
|
|
void Entry::updateChatListSortPosition() {
|
2019-04-30 10:01:34 +00:00
|
|
|
if (session().supportMode()
|
2018-09-29 12:18:26 +00:00
|
|
|
&& _sortKeyInChatList != 0
|
2019-04-30 10:01:34 +00:00
|
|
|
&& session().settings().supportFixChatsOrder()) {
|
2018-12-30 09:16:50 +00:00
|
|
|
updateChatListEntry();
|
2018-09-29 12:18:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-03-09 11:17:56 +00:00
|
|
|
_sortKeyByDate = DialogPosFromDate(adjustedChatListTimeId());
|
2019-04-18 11:31:30 +00:00
|
|
|
const auto fixedIndex = fixedOnTopIndex();
|
|
|
|
_sortKeyInChatList = fixedIndex
|
|
|
|
? FixedOnTopDialogPos(fixedIndex)
|
2020-03-17 13:04:30 +00:00
|
|
|
: computeSortPosition(0);
|
2018-01-23 16:51:12 +00:00
|
|
|
if (needUpdateInChatList()) {
|
|
|
|
setChatListExistence(true);
|
2019-04-30 10:01:34 +00:00
|
|
|
} else {
|
2020-03-09 11:17:56 +00:00
|
|
|
_sortKeyInChatList = _sortKeyByDate = 0;
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-17 13:04:30 +00:00
|
|
|
int Entry::lookupPinnedIndex(FilterId filterId) const {
|
|
|
|
if (filterId) {
|
|
|
|
const auto i = _pinnedIndex.find(filterId);
|
|
|
|
return (i != end(_pinnedIndex)) ? i->second : 0;
|
|
|
|
} else if (!_pinnedIndex.empty()) {
|
|
|
|
return _pinnedIndex.front().first
|
|
|
|
? 0
|
|
|
|
: _pinnedIndex.front().second;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64 Entry::computeSortPosition(FilterId filterId) const {
|
|
|
|
const auto index = lookupPinnedIndex(filterId);
|
|
|
|
return index ? PinnedDialogPos(index) : _sortKeyByDate;
|
|
|
|
}
|
|
|
|
|
2018-01-23 16:51:12 +00:00
|
|
|
void Entry::updateChatListExistence() {
|
|
|
|
setChatListExistence(shouldBeInChatList());
|
|
|
|
}
|
|
|
|
|
2019-04-22 14:22:39 +00:00
|
|
|
void Entry::notifyUnreadStateChange(const UnreadState &wasState) {
|
2020-03-16 10:20:18 +00:00
|
|
|
Expects(folderKnown());
|
|
|
|
Expects(inChatList());
|
|
|
|
|
|
|
|
const auto nowState = chatListUnreadState();
|
|
|
|
owner().chatsList(folder())->unreadStateChanged(wasState, nowState);
|
|
|
|
auto &filters = owner().chatsFilters();
|
|
|
|
for (const auto &[filterId, links] : _chatListLinks) {
|
|
|
|
filters.chatsList(filterId)->unreadStateChanged(wasState, nowState);
|
|
|
|
}
|
2019-04-22 14:22:39 +00:00
|
|
|
}
|
|
|
|
|
2018-01-23 16:51:12 +00:00
|
|
|
void Entry::setChatListExistence(bool exists) {
|
2020-06-12 14:09:04 +00:00
|
|
|
if (exists && _sortKeyInChatList) {
|
|
|
|
owner().refreshChatListEntry(this);
|
|
|
|
updateChatListEntry();
|
|
|
|
} else {
|
|
|
|
owner().removeChatListEntry(this);
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-18 09:00:38 +00:00
|
|
|
TimeId Entry::adjustedChatListTimeId() const {
|
2019-01-15 11:57:45 +00:00
|
|
|
return chatListTimeId();
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::changedChatListPinHook() {
|
|
|
|
}
|
|
|
|
|
2020-02-07 09:43:12 +00:00
|
|
|
RowsByLetter *Entry::chatListLinks(FilterId filterId) {
|
|
|
|
const auto i = _chatListLinks.find(filterId);
|
|
|
|
return (i != end(_chatListLinks)) ? &i->second : nullptr;
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 09:43:12 +00:00
|
|
|
const RowsByLetter *Entry::chatListLinks(FilterId filterId) const {
|
|
|
|
const auto i = _chatListLinks.find(filterId);
|
|
|
|
return (i != end(_chatListLinks)) ? &i->second : nullptr;
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 09:43:12 +00:00
|
|
|
not_null<Row*> Entry::mainChatListLink(FilterId filterId) const {
|
|
|
|
const auto links = chatListLinks(filterId);
|
|
|
|
Assert(links != nullptr);
|
|
|
|
return links->main;
|
2018-01-23 16:51:12 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 09:43:12 +00:00
|
|
|
Row *Entry::maybeMainChatListLink(FilterId filterId) const {
|
|
|
|
const auto links = chatListLinks(filterId);
|
|
|
|
return links ? links->main.get() : nullptr;
|
|
|
|
}
|
|
|
|
|
2020-03-16 10:20:18 +00:00
|
|
|
PositionChange Entry::adjustByPosInChatList(
|
|
|
|
FilterId filterId,
|
|
|
|
not_null<MainList*> list) {
|
2020-02-07 09:43:12 +00:00
|
|
|
const auto links = chatListLinks(filterId);
|
|
|
|
Assert(links != nullptr);
|
|
|
|
const auto from = links->main->pos();
|
2020-03-16 10:20:18 +00:00
|
|
|
list->indexed()->adjustByDate(*links);
|
2020-02-07 09:43:12 +00:00
|
|
|
const auto to = links->main->pos();
|
2019-04-16 14:05:56 +00:00
|
|
|
return { from, to };
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 11:57:45 +00:00
|
|
|
void Entry::setChatListTimeId(TimeId date) {
|
|
|
|
_timeId = date;
|
2018-01-05 15:57:18 +00:00
|
|
|
updateChatListSortPosition();
|
2019-04-18 09:00:38 +00:00
|
|
|
if (const auto folder = this->folder()) {
|
|
|
|
folder->updateChatListSortPosition();
|
|
|
|
}
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 09:43:12 +00:00
|
|
|
int Entry::posInChatList(FilterId filterId) const {
|
|
|
|
return mainChatListLink(filterId)->pos();
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-16 10:20:18 +00:00
|
|
|
not_null<Row*> Entry::addToChatList(
|
|
|
|
FilterId filterId,
|
|
|
|
not_null<MainList*> list) {
|
2020-02-07 09:43:12 +00:00
|
|
|
if (const auto main = maybeMainChatListLink(filterId)) {
|
|
|
|
return main;
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
2020-03-16 10:20:18 +00:00
|
|
|
return _chatListLinks.emplace(
|
2020-02-07 09:43:12 +00:00
|
|
|
filterId,
|
2020-06-12 14:09:04 +00:00
|
|
|
list->addEntry(this)
|
2020-02-07 09:43:12 +00:00
|
|
|
).first->second.main;
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-16 10:20:18 +00:00
|
|
|
void Entry::removeFromChatList(
|
|
|
|
FilterId filterId,
|
|
|
|
not_null<MainList*> list) {
|
2020-03-29 08:33:55 +00:00
|
|
|
if (isPinnedDialog(filterId)) {
|
2020-06-12 14:09:04 +00:00
|
|
|
owner().setChatPinned(this, filterId, false);
|
2020-03-29 08:33:55 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 09:43:12 +00:00
|
|
|
const auto i = _chatListLinks.find(filterId);
|
|
|
|
if (i == end(_chatListLinks)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_chatListLinks.erase(i);
|
2020-06-12 14:09:04 +00:00
|
|
|
list->removeEntry(this);
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 09:43:12 +00:00
|
|
|
void Entry::removeChatListEntryByLetter(FilterId filterId, QChar letter) {
|
|
|
|
const auto i = _chatListLinks.find(filterId);
|
|
|
|
if (i != end(_chatListLinks)) {
|
|
|
|
i->second.letters.remove(letter);
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::addChatListEntryByLetter(
|
2020-02-07 09:43:12 +00:00
|
|
|
FilterId filterId,
|
2018-01-05 15:57:18 +00:00
|
|
|
QChar letter,
|
|
|
|
not_null<Row*> row) {
|
2020-02-07 09:43:12 +00:00
|
|
|
const auto i = _chatListLinks.find(filterId);
|
|
|
|
if (i != end(_chatListLinks)) {
|
|
|
|
i->second.letters.emplace(letter, row);
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-12 14:09:04 +00:00
|
|
|
void Entry::updateChatListEntry() {
|
|
|
|
session().changes().entryUpdated(this, Data::EntryUpdate::Flag::Repaint);
|
2018-01-05 15:57:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Dialogs
|