/* 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 "data/data_folder.h" #include "data/data_session.h" #include "data/data_channel.h" #include "dialogs/dialogs_key.h" #include "history/history.h" #include "history/history_item.h" #include "lang/lang_keys.h" #include "storage/storage_facade.h" //#include "storage/storage_feed_messages.h" // #feed #include "auth_session.h" #include "apiwrap.h" #include "mainwidget.h" #include "styles/style_dialogs.h" // st::dialogsArchiveUserpic namespace Data { namespace { constexpr auto kLoadedChatsMinCount = 20; } // namespace // #feed //MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position) { // Expects(position.type() == mtpc_feedPosition); // // const auto &data = position.c_feedPosition(); // return MessagePosition(data.vdate.v, FullMsgId( // peerToChannel(peerFromMTP(data.vpeer)), // data.vid.v)); //} Folder::Folder(not_null owner, FolderId id) : Entry(owner, this) , _id(id) , _chatsList(Dialogs::SortMode::Date) , _name(lang(lng_archived_chats)) { indexNameParts(); } FolderId Folder::id() const { return _id; } void Folder::indexNameParts() { _nameWords.clear(); _nameFirstLetters.clear(); auto toIndexList = QStringList(); auto appendToIndex = [&](const QString &value) { if (!value.isEmpty()) { toIndexList.push_back(TextUtilities::RemoveAccents(value)); } }; appendToIndex(_name); const auto appendTranslit = !toIndexList.isEmpty() && cRussianLetters().match(toIndexList.front()).hasMatch(); if (appendTranslit) { appendToIndex(translitRusEng(toIndexList.front())); } auto toIndex = toIndexList.join(' '); toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex); const auto namesList = TextUtilities::PrepareSearchWords(toIndex); for (const auto &name : namesList) { _nameWords.insert(name); _nameFirstLetters.insert(name[0]); } } void Folder::registerOne(not_null history) { if (_chatsList.contains(history)) { return; } const auto invisible = _chatsList.empty(); _chatsList.addToEnd(history); //session().storage().invalidate( // #feed // Storage::FeedMessagesInvalidate(_id)); if (history->chatListMessageKnown()) { if (const auto last = history->chatListMessage()) { if (justUpdateChatListMessage(last)) { updateChatListEntry(); } } } else if (chatListMessageKnown()) { history->requestChatListMessage(); } if (unreadCountKnown()) { if (history->unreadCountKnown()) { // If history unreadCount is known that means that we've // already had the channel information and if it was in the // feed already (not yet known) it wouldn't get here. // That means here we get if we add a new channel to feed. if (const auto count = history->unreadCount()) { unreadCountChanged(count, history->mute() ? count : 0); } } else if (!_settingHistories) { session().api().requestDialogEntry(this); } } if (invisible && !_chatsList.empty()) { updateChatListExistence(); //for (const auto history : _histories) { // #TODO archived // history->updateChatListExistence(); //} } else { history->updateChatListExistence(); } owner().notifyFolderUpdated(this, FolderUpdateFlag::List); } void Folder::unregisterOne(not_null history) { if (!_chatsList.contains(history)) { return; } _chatsList.del(history); //session().storage().remove( // #feed // Storage::FeedMessagesRemoveAll(_id, channel->bareId())); if (chatListMessageKnown()) { if (const auto last = chatListMessage()) { if (last->history() == history) { recountChatListMessage(); } } } if (unreadCountKnown()) { if (history->unreadCountKnown()) { if (const auto delta = -history->unreadCount()) { unreadCountChanged(delta, history->mute() ? delta : 0); } } else { session().api().requestDialogEntry(this); } } if (_chatsList.empty()) { updateChatListExistence(); //for (const auto history : _histories) { // #TODO archive // history->updateChatListExistence(); //} } else { history->updateChatListExistence(); } owner().notifyFolderUpdated(this, FolderUpdateFlag::List); } not_null Folder::chatsList() { return &_chatsList; } void Folder::updateChatListMessage(not_null item) { if (justUpdateChatListMessage(item)) { if (_chatListMessage && *_chatListMessage) { setChatListTimeId((*_chatListMessage)->date()); } } } void Folder::loadUserpic() { //constexpr auto kPaintUserpicsCount = 4; // #feed //auto load = kPaintUserpicsCount; //for (const auto history : _histories) { // history->peer->loadUserpic(); // if (!--load) { // break; // } //} } void Folder::paintUserpic( Painter &p, int x, int y, int size) const { p.setPen(Qt::NoPen); p.setBrush(st::historyPeerArchiveUserpicBg); { PainterHighQualityEnabler hq(p); p.drawRoundedRect(x, y, size, size, size / 3., size / 3.); } st::dialogsArchiveUserpic.paintInCenter(p, { x, y, size, size }); //const auto small = (size - st::lineWidth) / 2; // #feed //const auto delta = size - small; //auto index = 0; //for (const auto history : _histories) { // history->peer->paintUserpic(p, x, y, small); // switch (++index) { // case 1: // case 3: x += delta; break; // case 2: x -= delta; y += delta; break; // case 4: return; // } //} } bool Folder::historiesLoaded() const { return _historiesLoaded; } void Folder::setHistoriesLoaded(bool loaded) { if (_historiesLoaded != loaded) { _historiesLoaded = loaded; owner().notifyFolderUpdated(this, FolderUpdateFlag::List); } } // // #feed //int32 Folder::chatsHash() const { // const auto ordered = ranges::view::all( // _histories // ) | ranges::view::transform([](not_null history) { // return history->peer->bareId(); // }) | ranges::to_vector | ranges::action::sort; // return Api::CountHash(ordered); //} // //void Folder::setChats(std::vector> chats) { // const auto remove = ranges::view::all( // _histories // ) | ranges::view::transform([](not_null history) { // return history->peer; // }) | ranges::view::filter([&](not_null peer) { // return !base::contains(chats, peer); // }) | ranges::to_vector; // // const auto add = ranges::view::all( // chats // ) | ranges::view::filter([&](not_null peer) { // return ranges::find( // _histories, // peer, // [](auto history) { return history->peer; } // ) == end(_histories); // }) | ranges::view::transform([](PeerData *peer) { // return not_null(peer); // }) | ranges::to_vector; // // changeChatsList(add, remove); // // setChatsLoaded(true); //} // //void Folder::changeChatsList( // const std::vector> &add, // const std::vector> &remove) { // _settingChats = true; // const auto restore = gsl::finally([&] { _settingChats = false; }); // // for (const auto channel : remove) { // channel->clearFeed(); // } // // //// We assume the last message was correct before requesting the list. // //// So we save it and don't allow channels from the list to change it. // //// After that we restore it. // const auto oldChatListMessage = base::take(_chatListMessage); // for (const auto channel : add) { // _chatListMessage = std::nullopt; // channel->setFeed(this); // } // _chatListMessage = oldChatListMessage; //} bool Folder::justUpdateChatListMessage(not_null item) { if (!_chatListMessage) { return false; } else if (*_chatListMessage && item->position() <= (*_chatListMessage)->position()) { return false; } _chatListMessage = item; return true; } void Folder::messageRemoved(not_null item) { if (chatListMessage() == item) { recountChatListMessage(); } } void Folder::historyCleared(not_null history) { if (const auto last = chatListMessage()) { if (last->history() == history) { messageRemoved(last); } } } void Folder::requestChatListMessage() { if (!chatListMessageKnown()) { session().api().requestDialogEntry(this); } } void Folder::recountChatListMessage() { _chatListMessage = std::nullopt; for (const auto entry : _chatsList) { if (entry->history() && !entry->history()->chatListMessageKnown()) { requestChatListMessage(); return; } } setChatListMessageFromChannels(); } void Folder::setChatListMessageFromChannels() { _chatListMessage = nullptr; for (const auto entry : _chatsList) { if (entry->history()) { if (const auto last = entry->history()->chatListMessage()) { justUpdateChatListMessage(last); } } } updateChatListDate(); } void Folder::updateChatListDate() { if (_chatListMessage && *_chatListMessage) { setChatListTimeId((*_chatListMessage)->date()); } } int Folder::unreadCount() const { return _unreadCount ? *_unreadCount : 0; } rpl::producer Folder::unreadCountValue() const { return rpl::single( unreadCount() ) | rpl::then(_unreadCountChanges.events()); } bool Folder::unreadCountKnown() const { return !!_unreadCount; } void Folder::applyDialog(const MTPDdialogFolder &data) { //const auto addChannel = [&](ChannelId channelId) { // #feed // if (const auto channel = owner().channelLoaded(channelId)) { // channel->setFeed(this); // } //}; //for (const auto &channelId : data.vfeed_other_channels.v) { // addChannel(channelId.v); //} _chatListMessage = nullptr; if (const auto peerId = peerFromMTP(data.vpeer)) { owner().history(peerId)->setFolder(this); const auto fullId = FullMsgId( peerToChannel(peerId), data.vtop_message.v); if (const auto item = App::histItemById(fullId)) { justUpdateChatListMessage(item); } } updateChatListDate(); setUnreadCounts( data.vunread_unmuted_messages_count.v, data.vunread_muted_messages_count.v); //setUnreadMark(data.is_unread_mark()); //setUnreadMentionsCount(data.vunread_mentions_count.v); //if (data.has_read_max_position()) { // #feed // setUnreadPosition(FeedPositionFromMTP(data.vread_max_position)); //} if (_chatsList.size() < kLoadedChatsMinCount) { session().api().requestFolderDialogs(_id); } } void Folder::changedInChatListHook(Dialogs::Mode list, bool added) { if (list != Dialogs::Mode::All) { return; } if (const auto count = unreadCount()) { const auto mutedCount = _unreadMutedCount; const auto nonMutedCount = count - mutedCount; const auto mutedDelta = added ? mutedCount : -mutedCount; const auto nonMutedDelta = added ? nonMutedCount : -nonMutedCount; owner().unreadIncrement(nonMutedDelta, false); owner().unreadIncrement(mutedDelta, true); const auto fullMuted = (nonMutedCount == 0); const auto entriesWithUnreadDelta = added ? 1 : -1; const auto mutedEntriesWithUnreadDelta = fullMuted ? entriesWithUnreadDelta : 0; owner().unreadEntriesChanged( entriesWithUnreadDelta, mutedEntriesWithUnreadDelta); } } template void Folder::updateUnreadCounts(PerformUpdate &&performUpdate) { const auto wasUnreadCount = _unreadCount ? *_unreadCount : 0; const auto wasUnreadMutedCount = _unreadMutedCount; const auto wasFullMuted = (wasUnreadMutedCount > 0) && (wasUnreadCount == wasUnreadMutedCount); performUpdate(); Assert(_unreadCount.has_value()); _unreadCountChanges.fire(unreadCount()); updateChatListEntry(); if (inChatList(Dialogs::Mode::All)) { const auto nowUnreadCount = *_unreadCount; const auto nowUnreadMutedCount = _unreadMutedCount; const auto nowFullMuted = (nowUnreadMutedCount > 0) && (nowUnreadCount == nowUnreadMutedCount); owner().unreadIncrement( (nowUnreadCount - nowUnreadMutedCount) - (wasUnreadCount - wasUnreadMutedCount), false); owner().unreadIncrement( nowUnreadMutedCount - wasUnreadMutedCount, true); const auto entriesDelta = (nowUnreadCount && !wasUnreadCount) ? 1 : (wasUnreadCount && !nowUnreadCount) ? -1 : 0; const auto mutedEntriesDelta = (!wasFullMuted && nowFullMuted) ? 1 : (wasFullMuted && !nowFullMuted) ? -1 : 0; owner().unreadEntriesChanged( entriesDelta, mutedEntriesDelta); } } void Folder::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) { if (unreadCountKnown() && (*_unreadCount == unreadNonMutedCount + unreadMutedCount) && (_unreadMutedCount == unreadMutedCount)) { return; } updateUnreadCounts([&] { _unreadCount = unreadNonMutedCount + unreadMutedCount; _unreadMutedCount = unreadMutedCount; }); } // #feed //void Folder::setUnreadPosition(const MessagePosition &position) { // if (_unreadPosition.current() < position) { // _unreadPosition = position; // } //} void Folder::unreadCountChanged(int unreadCountDelta, int mutedCountDelta) { if (!unreadCountKnown()) { return; } updateUnreadCounts([&] { accumulate_max(unreadCountDelta, -*_unreadCount); *_unreadCount += unreadCountDelta; mutedCountDelta = snap( mutedCountDelta, -_unreadMutedCount, *_unreadCount - _unreadMutedCount); _unreadMutedCount += mutedCountDelta; }); } // //void Folder::setUnreadMark(bool unread) { // if (_unreadMark != unread) { // _unreadMark = unread; // if (!_unreadCount || !*_unreadCount) { // if (inChatList(Dialogs::Mode::All)) { // const auto delta = _unreadMark ? 1 : -1; // owner().unreadIncrement(delta, mute()); // owner().unreadEntriesChanged( // delta, // mute() ? delta : 0); // // updateChatListEntry(); // } // } // Notify::peerUpdatedDelayed( // peer, // Notify::PeerUpdate::Flag::UnreadViewChanged); // } //} // //bool Folder::unreadMark() const { // return _unreadMark; //} // #feed //MessagePosition Folder::unreadPosition() const { // return _unreadPosition.current(); //} // //rpl::producer Folder::unreadPositionChanges() const { // return _unreadPosition.changes(); //} bool Folder::toImportant() const { return false; // TODO feeds workmode } bool Folder::useProxyPromotion() const { return false; } bool Folder::shouldBeInChatList() const { return !_chatsList.empty(); } int Folder::chatListUnreadCount() const { return unreadCount(); } bool Folder::chatListUnreadMark() const { return false; // #feed unread mark } bool Folder::chatListMutedBadge() const { return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false; } HistoryItem *Folder::chatListMessage() const { return _chatListMessage ? *_chatListMessage : nullptr; } bool Folder::chatListMessageKnown() const { return _chatListMessage.has_value(); } const QString &Folder::chatListName() const { return _name; } const base::flat_set &Folder::chatListNameWords() const { return _nameWords; } const base::flat_set &Folder::chatListFirstLetters() const { return _nameFirstLetters; } } // namespace Data