| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- /*
- 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"
- #include "data/data_changes.h"
- #include "data/data_session.h"
- #include "data/data_folder.h"
- #include "data/data_forum_topic.h"
- #include "data/data_chat_filters.h"
- #include "data/data_saved_sublist.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "mainwidget.h"
- #include "main/main_session.h"
- #include "main/main_session_settings.h"
- #include "ui/text/text_options.h"
- #include "ui/ui_utility.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "styles/style_dialogs.h" // st::dialogsTextWidthMin
- namespace Dialogs {
- namespace {
- auto DialogsPosToTopShift = 0;
- uint64 DialogPosFromDate(TimeId date) {
- if (!date) {
- return 0;
- }
- return (uint64(date) << 32) | (++DialogsPosToTopShift);
- }
- uint64 FixedOnTopDialogPos(int index) {
- return 0xFFFFFFFFFFFF000FULL - index;
- }
- uint64 PinnedDialogPos(int pinnedIndex) {
- return 0xFFFFFFFF000000FFULL - pinnedIndex;
- }
- } // namespace
- BadgesState BadgesForUnread(
- const UnreadState &state,
- CountInBadge count,
- IncludeInBadge include) {
- const auto countMessages = (count == CountInBadge::Messages)
- || ((count == CountInBadge::Default)
- && Core::App().settings().countUnreadMessages());
- const auto counterFull = state.marks
- + (countMessages ? state.messages : state.chats);
- const auto counterMuted = state.marksMuted
- + (countMessages ? state.messagesMuted : state.chatsMuted);
- const auto unreadMuted = (counterFull <= counterMuted);
- const auto includeMuted = (include == IncludeInBadge::All)
- || (include == IncludeInBadge::UnmutedOrAll && unreadMuted)
- || ((include == IncludeInBadge::Default)
- && Core::App().settings().includeMutedCounter());
- const auto marks = state.marks - (includeMuted ? 0 : state.marksMuted);
- const auto counter = counterFull - (includeMuted ? 0 : counterMuted);
- const auto mark = (counter == 1) && (marks == 1);
- return {
- .unreadCounter = mark ? 0 : counter,
- .unread = (counter > 0),
- .unreadMuted = includeMuted && (counter <= counterMuted),
- .mention = (state.mentions > 0),
- .reaction = (state.reactions > 0),
- .reactionMuted = (state.reactions <= state.reactionsMuted),
- };
- }
- Entry::Entry(not_null<Data::Session*> owner, Type type)
- : _owner(owner)
- , _flags((type == Type::History)
- ? (Flag::IsThread | Flag::IsHistory)
- : (type == Type::ForumTopic)
- ? Flag::IsThread
- : (type == Type::SavedSublist)
- ? Flag::IsSavedSublist
- : Flag(0)) {
- }
- Entry::~Entry() = default;
- Data::Session &Entry::owner() const {
- return *_owner;
- }
- Main::Session &Entry::session() const {
- return _owner->session();
- }
- History *Entry::asHistory() {
- return (_flags & Flag::IsHistory)
- ? static_cast<History*>(this)
- : nullptr;
- }
- Data::Forum *Entry::asForum() {
- return (_flags & Flag::IsHistory)
- ? static_cast<History*>(this)->peer->forum()
- : nullptr;
- }
- Data::Folder *Entry::asFolder() {
- return (_flags & (Flag::IsThread | Flag::IsSavedSublist))
- ? nullptr
- : static_cast<Data::Folder*>(this);
- }
- Data::Thread *Entry::asThread() {
- return (_flags & Flag::IsThread)
- ? static_cast<Data::Thread*>(this)
- : nullptr;
- }
- Data::ForumTopic *Entry::asTopic() {
- return ((_flags & Flag::IsThread) && !(_flags & Flag::IsHistory))
- ? static_cast<Data::ForumTopic*>(this)
- : nullptr;
- }
- Data::SavedSublist *Entry::asSublist() {
- return (_flags & Flag::IsSavedSublist)
- ? static_cast<Data::SavedSublist*>(this)
- : nullptr;
- }
- const History *Entry::asHistory() const {
- return const_cast<Entry*>(this)->asHistory();
- }
- const Data::Forum *Entry::asForum() const {
- return const_cast<Entry*>(this)->asForum();
- }
- const Data::Folder *Entry::asFolder() const {
- return const_cast<Entry*>(this)->asFolder();
- }
- const Data::Thread *Entry::asThread() const {
- return const_cast<Entry*>(this)->asThread();
- }
- const Data::ForumTopic *Entry::asTopic() const {
- return const_cast<Entry*>(this)->asTopic();
- }
- const Data::SavedSublist *Entry::asSublist() const {
- return const_cast<Entry*>(this)->asSublist();
- }
- void Entry::pinnedIndexChanged(FilterId filterId, int was, int now) {
- if (!filterId && 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);
- } else if (!was) {
- _pinnedIndex.emplace(filterId, index);
- } else {
- i->second = index;
- }
- pinnedIndexChanged(filterId, was, index);
- }
- bool Entry::needUpdateInChatList() const {
- return inChatList() || shouldBeInChatList();
- }
- void Entry::updateChatListSortPosition() {
- if (session().supportMode()
- && _sortKeyInChatList != 0
- && session().settings().supportFixChatsOrder()) {
- updateChatListEntry();
- return;
- }
- _sortKeyByDate = DialogPosFromDate(adjustedChatListTimeId());
- const auto fixedIndex = fixedOnTopIndex();
- _sortKeyInChatList = fixedIndex
- ? FixedOnTopDialogPos(fixedIndex)
- : computeSortPosition(0);
- if (needUpdateInChatList()) {
- setChatListExistence(true);
- } else {
- _sortKeyInChatList = _sortKeyByDate = 0;
- }
- }
- int Entry::lookupPinnedIndex(FilterId filterId) const {
- if (filterId) {
- const auto i = _pinnedIndex.find(filterId);
- return (i != end(_pinnedIndex)) ? i->second : 0;
- } else if (!_pinnedIndex.empty()) {
- return _pinnedIndex.front().first
- ? 0
- : _pinnedIndex.front().second;
- }
- return 0;
- }
- uint64 Entry::computeSortPosition(FilterId filterId) const {
- const auto index = lookupPinnedIndex(filterId);
- return index ? PinnedDialogPos(index) : _sortKeyByDate;
- }
- void Entry::updateChatListExistence() {
- setChatListExistence(shouldBeInChatList());
- }
- void Entry::notifyUnreadStateChange(const UnreadState &wasState) {
- Expects(folderKnown());
- Expects(inChatList());
- const auto nowState = chatListUnreadState();
- owner().chatsListFor(this)->unreadStateChanged(wasState, nowState);
- auto &filters = owner().chatsFilters();
- for (const auto &[filterId, links] : _chatListLinks) {
- if (filterId) {
- filters.chatsList(filterId)->unreadStateChanged(
- wasState,
- nowState);
- }
- }
- if (const auto history = asHistory()) {
- session().changes().historyUpdated(
- history,
- Data::HistoryUpdate::Flag::UnreadView);
- const auto isForFilters = [](UnreadState state) {
- return state.messages || state.marks || state.mentions;
- };
- if (isForFilters(wasState) != isForFilters(nowState)) {
- const auto wasTags = _tagColors.size();
- owner().chatsFilters().refreshHistory(history);
- // Hack for History::fakeUnreadWhileOpened().
- if (!isForFilters(nowState)
- && (wasTags > 0)
- && (wasTags == _tagColors.size())) {
- auto updateRequested = false;
- for (const auto &filter : filters.list()) {
- if (!(filter.flags() & Data::ChatFilter::Flag::NoRead)
- || !_chatListLinks.contains(filter.id())
- || filter.contains(history, true)) {
- continue;
- }
- const auto wasTagsCount = _tagColors.size();
- setColorIndexForFilterId(filter.id(), std::nullopt);
- updateRequested |= (wasTagsCount != _tagColors.size());
- }
- if (updateRequested) {
- updateChatListEntryHeight();
- session().changes().peerUpdated(
- history->peer,
- Data::PeerUpdate::Flag::Name);
- }
- }
- }
- }
- updateChatListEntryPostponed();
- }
- const Ui::Text::String &Entry::chatListNameText() const {
- const auto version = chatListNameVersion();
- if (_chatListNameVersion < version) {
- _chatListNameVersion = version;
- _chatListNameText.setText(
- st::semiboldTextStyle,
- chatListName(),
- Ui::NameTextOptions());
- }
- return _chatListNameText;
- }
- void Entry::setChatListExistence(bool exists) {
- if (exists && _sortKeyInChatList) {
- owner().refreshChatListEntry(this);
- updateChatListEntry();
- } else {
- owner().removeChatListEntry(this);
- }
- }
- TimeId Entry::adjustedChatListTimeId() const {
- return chatListTimeId();
- }
- void Entry::changedChatListPinHook() {
- }
- RowsByLetter *Entry::chatListLinks(FilterId filterId) {
- const auto i = _chatListLinks.find(filterId);
- return (i != end(_chatListLinks)) ? &i->second : nullptr;
- }
- const RowsByLetter *Entry::chatListLinks(FilterId filterId) const {
- const auto i = _chatListLinks.find(filterId);
- return (i != end(_chatListLinks)) ? &i->second : nullptr;
- }
- not_null<Row*> Entry::mainChatListLink(FilterId filterId) const {
- const auto links = chatListLinks(filterId);
- Assert(links != nullptr);
- return links->main;
- }
- Row *Entry::maybeMainChatListLink(FilterId filterId) const {
- const auto links = chatListLinks(filterId);
- return links ? links->main.get() : nullptr;
- }
- PositionChange Entry::adjustByPosInChatList(
- FilterId filterId,
- not_null<MainList*> list) {
- const auto links = chatListLinks(filterId);
- Assert(links != nullptr);
- const auto from = links->main->top();
- list->indexed()->adjustByDate(*links);
- const auto to = links->main->top();
- return { .from = from, .to = to, .height = links->main->height() };
- }
- void Entry::setChatListTimeId(TimeId date) {
- _timeId = date;
- updateChatListSortPosition();
- if (const auto folder = this->folder()) {
- folder->updateChatListSortPosition();
- }
- }
- int Entry::posInChatList(FilterId filterId) const {
- return mainChatListLink(filterId)->index();
- }
- void Entry::setColorIndexForFilterId(
- FilterId filterId,
- std::optional<uint8> colorIndex) {
- if (!filterId) {
- return;
- }
- if (colorIndex) {
- _tagColors[filterId] = *colorIndex;
- } else {
- _tagColors.remove(filterId);
- }
- }
- not_null<Row*> Entry::addToChatList(
- FilterId filterId,
- not_null<MainList*> list) {
- if (filterId) {
- const auto &list = owner().chatsFilters().list();
- const auto it = ranges::find(list, filterId, &Data::ChatFilter::id);
- if (it != end(list)) {
- setColorIndexForFilterId(filterId, it->colorIndex());
- }
- }
- if (const auto main = maybeMainChatListLink(filterId)) {
- return main;
- }
- return _chatListLinks.emplace(
- filterId,
- list->addEntry(this)
- ).first->second.main;
- }
- void Entry::removeFromChatList(
- FilterId filterId,
- not_null<MainList*> list) {
- if (isPinnedDialog(filterId)) {
- owner().setChatPinned(this, filterId, false);
- }
- if (filterId) {
- const auto it = _tagColors.find(filterId);
- if (it != end(_tagColors)) {
- _tagColors.erase(it);
- }
- }
- const auto i = _chatListLinks.find(filterId);
- if (i == end(_chatListLinks)) {
- return;
- }
- _chatListLinks.erase(i);
- list->removeEntry(this);
- }
- void Entry::removeChatListEntryByLetter(FilterId filterId, QChar letter) {
- const auto i = _chatListLinks.find(filterId);
- if (i != end(_chatListLinks)) {
- i->second.letters.remove(letter);
- }
- }
- void Entry::addChatListEntryByLetter(
- FilterId filterId,
- QChar letter,
- not_null<Row*> row) {
- const auto i = _chatListLinks.find(filterId);
- if (i != end(_chatListLinks)) {
- i->second.letters.emplace(letter, row);
- }
- }
- void Entry::updateChatListEntry() {
- _flags &= ~Flag::UpdatePostponed;
- session().changes().entryUpdated(this, Data::EntryUpdate::Flag::Repaint);
- }
- void Entry::updateChatListEntryPostponed() {
- if (_flags & Flag::UpdatePostponed) {
- return;
- }
- _flags |= Flag::UpdatePostponed;
- Ui::PostponeCall(this, [=] {
- if (_flags & Flag::UpdatePostponed) {
- updateChatListEntry();
- }
- });
- }
- void Entry::updateChatListEntryHeight() {
- session().changes().entryUpdated(this, Data::EntryUpdate::Flag::Height);
- }
- [[nodiscard]] bool Entry::hasChatsFilterTags(FilterId exclude) const {
- if (!owner().chatsFilters().tagsEnabled()) {
- return false;
- }
- if (exclude) {
- if (_tagColors.size() == 1) {
- if (_tagColors.begin()->first == exclude) {
- return false;
- }
- }
- }
- return !_tagColors.empty();
- }
- } // namespace Dialogs
|