| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- 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 "data/data_histories.h"
- #include "data/data_changes.h"
- #include "dialogs/dialogs_key.h"
- #include "dialogs/ui/dialogs_layout.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "ui/painter.h"
- #include "ui/text/text_options.h"
- #include "ui/text/text_utilities.h"
- #include "lang/lang_keys.h"
- #include "storage/storage_facade.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "main/main_account.h"
- #include "main/main_session.h"
- #include "mtproto/mtproto_config.h"
- #include "apiwrap.h"
- #include "mainwidget.h"
- #include "styles/style_dialogs.h"
- namespace Data {
- namespace {
- constexpr auto kLoadedChatsMinCount = 20;
- constexpr auto kShowChatNamesCount = 8;
- [[nodiscard]] TextWithEntities ComposeFolderListEntryText(
- not_null<Folder*> folder) {
- const auto &list = folder->lastHistories();
- if (list.empty()) {
- if (const auto storiesUnread = folder->storiesUnreadCount()) {
- return {
- tr::lng_contacts_stories_status_new(
- tr::now,
- lt_count,
- storiesUnread),
- };
- } else if (const auto storiesCount = folder->storiesCount()) {
- return {
- tr::lng_contacts_stories_status(
- tr::now,
- lt_count,
- storiesCount),
- };
- }
- return {};
- }
- const auto count = std::max(
- int(list.size()),
- folder->chatsList()->fullSize().current());
- const auto throwAwayLastName = (list.size() > 1)
- && (count == list.size() + 1);
- auto &&peers = ranges::views::all(
- list
- ) | ranges::views::take(
- list.size() - (throwAwayLastName ? 1 : 0)
- );
- const auto wrapName = [](not_null<History*> history) {
- const auto name = history->peer->name();
- return st::wrap_rtl(TextWithEntities{
- .text = name,
- .entities = (history->chatListBadgesState().unread
- ? EntitiesInText{
- { EntityType::Semibold, 0, int(name.size()), QString() },
- { EntityType::Colorized, 0, int(name.size()), QString() },
- }
- : EntitiesInText{}),
- });
- };
- const auto shown = int(peers.size());
- const auto accumulated = [&] {
- Expects(shown > 0);
- auto i = peers.begin();
- auto result = wrapName(*i);
- for (++i; i != peers.end(); ++i) {
- result = tr::lng_archived_last_list(
- tr::now,
- lt_accumulated,
- result,
- lt_chat,
- wrapName(*i),
- Ui::Text::WithEntities);
- }
- return result;
- }();
- return (shown < count)
- ? tr::lng_archived_last(
- tr::now,
- lt_count,
- (count - shown),
- lt_chats,
- accumulated,
- Ui::Text::WithEntities)
- : accumulated;
- }
- } // namespace
- Folder::Folder(not_null<Session*> owner, FolderId id)
- : Entry(owner, Type::Folder)
- , _id(id)
- , _chatsList(
- &owner->session(),
- FilterId(),
- owner->maxPinnedChatsLimitValue(this))
- , _name(tr::lng_archived_name(tr::now)) {
- indexNameParts();
- session().changes().peerUpdates(
- PeerUpdate::Flag::Name
- ) | rpl::filter([=](const PeerUpdate &update) {
- return ranges::contains(_lastHistories, update.peer, &History::peer);
- }) | rpl::start_with_next([=] {
- ++_chatListViewVersion;
- updateChatListEntryPostponed();
- }, _lifetime);
- _chatsList.setAllAreMuted(true);
- _chatsList.unreadStateChanges(
- ) | rpl::filter([=] {
- return inChatList();
- }) | rpl::start_with_next([=](const Dialogs::UnreadState &old) {
- ++_chatListViewVersion;
- notifyUnreadStateChange(old);
- }, _lifetime);
- _chatsList.fullSize().changes(
- ) | rpl::start_with_next([=] {
- updateChatListEntryPostponed();
- }, _lifetime);
- }
- FolderId Folder::id() const {
- return _id;
- }
- void Folder::indexNameParts() {
- // We don't want archive to be filtered in the chats list.
- }
- void Folder::registerOne(not_null<History*> history) {
- if (_chatsList.indexed()->size() == 1) {
- updateChatListSortPosition();
- if (!_chatsList.cloudUnreadKnown()) {
- owner().histories().requestDialogEntry(this);
- }
- } else {
- updateChatListEntry();
- }
- reorderLastHistories();
- }
- void Folder::unregisterOne(not_null<History*> history) {
- if (_chatsList.empty()) {
- updateChatListExistence();
- }
- reorderLastHistories();
- }
- int Folder::chatListNameVersion() const {
- return 1;
- }
- void Folder::oneListMessageChanged(HistoryItem *from, HistoryItem *to) {
- if (from || to) {
- reorderLastHistories();
- }
- }
- void Folder::reorderLastHistories() {
- // We want first kShowChatNamesCount histories, by last message date.
- const auto pred = [](not_null<History*> a, not_null<History*> b) {
- const auto aItem = a->chatListMessage();
- const auto bItem = b->chatListMessage();
- const auto aDate = aItem ? aItem->date() : TimeId(0);
- const auto bDate = bItem ? bItem->date() : TimeId(0);
- return aDate > bDate;
- };
- _lastHistories.clear();
- _lastHistories.reserve(kShowChatNamesCount + 1);
- auto &&histories = ranges::views::all(
- *_chatsList.indexed()
- ) | ranges::views::transform([](not_null<Dialogs::Row*> row) {
- return row->history();
- }) | ranges::views::filter([](History *history) {
- return (history != nullptr);
- });
- auto nonPinnedChecked = 0;
- for (const auto history : histories) {
- const auto i = ranges::upper_bound(
- _lastHistories,
- not_null(history),
- pred);
- if (size(_lastHistories) < kShowChatNamesCount
- || i != end(_lastHistories)) {
- _lastHistories.insert(i, history);
- }
- if (size(_lastHistories) > kShowChatNamesCount) {
- _lastHistories.pop_back();
- }
- if (!history->isPinnedDialog(FilterId())
- && ++nonPinnedChecked >= kShowChatNamesCount) {
- break;
- }
- }
- ++_chatListViewVersion;
- updateChatListEntry();
- }
- not_null<Dialogs::MainList*> Folder::chatsList() {
- return &_chatsList;
- }
- void Folder::clearChatsList() {
- _chatsList.clear();
- }
- void Folder::chatListPreloadData() {
- }
- void Folder::paintUserpic(
- Painter &p,
- Ui::PeerUserpicView &view,
- const Dialogs::Ui::PaintContext &context) const {
- paintUserpic(
- p,
- context.st->padding.left(),
- context.st->padding.top(),
- context.st->photoSize);
- }
- void Folder::paintUserpic(Painter &p, int x, int y, int size) const {
- paintUserpic(p, x, y, size, nullptr, nullptr);
- }
- void Folder::paintUserpic(
- Painter &p,
- int x,
- int y,
- int size,
- const style::color &bg,
- const style::color &fg) const {
- paintUserpic(p, x, y, size, &bg, &fg);
- }
- void Folder::paintUserpic(
- Painter &p,
- int x,
- int y,
- int size,
- const style::color *overrideBg,
- const style::color *overrideFg) const {
- p.setPen(Qt::NoPen);
- p.setBrush(overrideBg ? *overrideBg : st::historyPeerArchiveUserpicBg);
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(x, y, size, size);
- }
- if (size == st::defaultDialogRow.photoSize) {
- const auto rect = QRect{ x, y, size, size };
- if (overrideFg) {
- st::dialogsArchiveUserpic.paintInCenter(
- p,
- rect,
- (*overrideFg)->c);
- } else {
- st::dialogsArchiveUserpic.paintInCenter(p, rect);
- }
- } else {
- p.save();
- const auto ratio = size / float64(st::defaultDialogRow.photoSize);
- p.translate(x + size / 2., y + size / 2.);
- p.scale(ratio, ratio);
- const auto skip = st::defaultDialogRow.photoSize;
- const auto rect = QRect{ -skip, -skip, 2 * skip, 2 * skip };
- if (overrideFg) {
- st::dialogsArchiveUserpic.paintInCenter(
- p,
- rect,
- (*overrideFg)->c);
- } else {
- st::dialogsArchiveUserpic.paintInCenter(p, rect);
- }
- p.restore();
- }
- }
- const std::vector<not_null<History*>> &Folder::lastHistories() const {
- return _lastHistories;
- }
- void Folder::validateListEntryCache() {
- if (_listEntryCacheVersion == _chatListViewVersion) {
- return;
- }
- _listEntryCacheVersion = _chatListViewVersion;
- _listEntryCache.setMarkedText(
- st::dialogsTextStyle,
- ComposeFolderListEntryText(this),
- // Use rich options as long as the entry text does not have user text.
- Ui::ItemTextDefaultOptions());
- }
- void Folder::updateStoriesCount(int count, int unread) {
- if (_storiesCount == count && _storiesUnreadCount == unread) {
- return;
- }
- const auto limit = (1 << 16) - 1;
- const auto was = (_storiesCount > 0);
- _storiesCount = std::min(count, limit);
- _storiesUnreadCount = std::min(unread, limit);
- const auto now = (_storiesCount > 0);
- if (was == now) {
- updateChatListEntryPostponed();
- } else if (now) {
- updateChatListSortPosition();
- } else {
- updateChatListExistence();
- }
- ++_chatListViewVersion;
- }
- int Folder::storiesCount() const {
- return _storiesCount;
- }
- int Folder::storiesUnreadCount() const {
- return _storiesUnreadCount;
- }
- TimeId Folder::adjustedChatListTimeId() const {
- return chatListTimeId();
- }
- void Folder::applyDialog(const MTPDdialogFolder &data) {
- _chatsList.updateCloudUnread(data);
- if (const auto peerId = peerFromMTP(data.vpeer())) {
- const auto history = owner().history(peerId);
- const auto fullId = FullMsgId(peerId, data.vtop_message().v);
- history->setFolder(this, owner().message(fullId));
- } else {
- _chatsList.clear();
- updateChatListExistence();
- }
- if (_chatsList.indexed()->size() < kLoadedChatsMinCount) {
- session().api().requestDialogs(this);
- }
- }
- void Folder::applyPinnedUpdate(const MTPDupdateDialogPinned &data) {
- const auto folderId = data.vfolder_id().value_or_empty();
- if (folderId != 0) {
- LOG(("API Error: Nested folders detected."));
- }
- owner().setChatPinned(this, FilterId(), data.is_pinned());
- }
- int Folder::fixedOnTopIndex() const {
- return kArchiveFixOnTopIndex;
- }
- bool Folder::shouldBeInChatList() const {
- return !_chatsList.empty() || (_storiesCount > 0);
- }
- Dialogs::UnreadState Folder::chatListUnreadState() const {
- return _chatsList.unreadState();
- }
- Dialogs::BadgesState Folder::chatListBadgesState() const {
- auto result = Dialogs::BadgesForUnread(
- chatListUnreadState(),
- Dialogs::CountInBadge::Chats,
- Dialogs::IncludeInBadge::All);
- result.unreadMuted = result.mentionMuted = result.reactionMuted = true;
- if (result.unread && !result.unreadCounter) {
- result.unreadCounter = 1;
- }
- return result;
- }
- HistoryItem *Folder::chatListMessage() const {
- return nullptr;
- }
- bool Folder::chatListMessageKnown() const {
- return true;
- }
- const QString &Folder::chatListName() const {
- return _name;
- }
- const base::flat_set<QString> &Folder::chatListNameWords() const {
- return _nameWords;
- }
- const base::flat_set<QChar> &Folder::chatListFirstLetters() const {
- return _nameFirstLetters;
- }
- const QString &Folder::chatListNameSortKey() const {
- static const auto empty = QString();
- return empty;
- }
- } // namespace Data
|