256 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 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_saved_sublist.h"
 | |
| 
 | |
| #include "data/data_histories.h"
 | |
| #include "data/data_peer.h"
 | |
| #include "data/data_saved_messages.h"
 | |
| #include "data/data_session.h"
 | |
| #include "history/view/history_view_item_preview.h"
 | |
| #include "history/history.h"
 | |
| #include "history/history_item.h"
 | |
| 
 | |
| namespace Data {
 | |
| 
 | |
| SavedSublist::SavedSublist(not_null<PeerData*> peer)
 | |
| : Entry(&peer->owner(), Dialogs::Entry::Type::SavedSublist)
 | |
| , _history(peer->owner().history(peer)) {
 | |
| }
 | |
| 
 | |
| SavedSublist::~SavedSublist() = default;
 | |
| 
 | |
| not_null<History*> SavedSublist::history() const {
 | |
| 	return _history;
 | |
| }
 | |
| 
 | |
| not_null<PeerData*> SavedSublist::peer() const {
 | |
| 	return _history->peer;
 | |
| }
 | |
| 
 | |
| bool SavedSublist::isHiddenAuthor() const {
 | |
| 	return peer()->isSavedHiddenAuthor();
 | |
| }
 | |
| 
 | |
| bool SavedSublist::isFullLoaded() const {
 | |
| 	return (_flags & Flag::FullLoaded) != 0;
 | |
| }
 | |
| 
 | |
| auto SavedSublist::messages() const
 | |
| -> const std::vector<not_null<HistoryItem*>> & {
 | |
| 	return _items;
 | |
| }
 | |
| 
 | |
| void SavedSublist::applyMaybeLast(not_null<HistoryItem*> item, bool added) {
 | |
| 	const auto before = [](
 | |
| 			not_null<HistoryItem*> a,
 | |
| 			not_null<HistoryItem*> b) {
 | |
| 		return IsServerMsgId(a->id)
 | |
| 			? (IsServerMsgId(b->id) ? (a->id < b->id) : true)
 | |
| 			: (IsServerMsgId(b->id) ? false : (a->id < b->id));
 | |
| 	};
 | |
| 
 | |
| 	if (_items.empty()) {
 | |
| 		_items.push_back(item);
 | |
| 	} else if (_items.front() == item) {
 | |
| 		return;
 | |
| 	} else if (!isFullLoaded()
 | |
| 		&& _items.size() == 1
 | |
| 		&& before(_items.front(), item)) {
 | |
| 		_items[0] = item;
 | |
| 	} else if (before(_items.back(), item)) {
 | |
| 		for (auto i = begin(_items); i != end(_items); ++i) {
 | |
| 			if (item == *i) {
 | |
| 				return;
 | |
| 			} else if (before(*i, item)) {
 | |
| 				_items.insert(i, item);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if (added && _fullCount) {
 | |
| 		++*_fullCount;
 | |
| 	}
 | |
| 	if (_items.front() == item) {
 | |
| 		setChatListTimeId(item->date());
 | |
| 		resolveChatListMessageGroup();
 | |
| 	}
 | |
| 	_changed.fire({});
 | |
| }
 | |
| 
 | |
| void SavedSublist::removeOne(not_null<HistoryItem*> item) {
 | |
| 	if (_items.empty()) {
 | |
| 		return;
 | |
| 	}
 | |
| 	const auto last = (_items.front() == item);
 | |
| 	const auto from = ranges::remove(_items, item);
 | |
| 	const auto removed = end(_items) - from;
 | |
| 	if (removed) {
 | |
| 		_items.erase(from, end(_items));
 | |
| 	}
 | |
| 	if (_fullCount) {
 | |
| 		--*_fullCount;
 | |
| 	}
 | |
| 	if (last) {
 | |
| 		if (_items.empty()) {
 | |
| 			if (isFullLoaded()) {
 | |
| 				updateChatListExistence();
 | |
| 			} else {
 | |
| 				updateChatListEntry();
 | |
| 				crl::on_main(this, [=] {
 | |
| 					owner().savedMessages().loadMore(this);
 | |
| 				});
 | |
| 			}
 | |
| 		} else {
 | |
| 			setChatListTimeId(_items.front()->date());
 | |
| 		}
 | |
| 	}
 | |
| 	if (removed || _fullCount) {
 | |
| 		_changed.fire({});
 | |
| 	}
 | |
| }
 | |
| 
 | |
| rpl::producer<> SavedSublist::changes() const {
 | |
| 	return _changed.events();
 | |
| }
 | |
| 
 | |
| std::optional<int> SavedSublist::fullCount() const {
 | |
| 	return isFullLoaded() ? int(_items.size()) : _fullCount;
 | |
| }
 | |
| 
 | |
| rpl::producer<int> SavedSublist::fullCountValue() const {
 | |
| 	return _changed.events_starting_with({}) | rpl::map([=] {
 | |
| 		return fullCount();
 | |
| 	}) | rpl::filter_optional();
 | |
| }
 | |
| 
 | |
| void SavedSublist::append(
 | |
| 		std::vector<not_null<HistoryItem*>> &&items,
 | |
| 		int fullCount) {
 | |
| 	_fullCount = fullCount;
 | |
| 	if (items.empty()) {
 | |
| 		setFullLoaded();
 | |
| 	} else if (_items.empty()) {
 | |
| 		_items = std::move(items);
 | |
| 		setChatListTimeId(_items.front()->date());
 | |
| 		_changed.fire({});
 | |
| 	} else if (_items.back()->id > items.front()->id) {
 | |
| 		_items.insert(end(_items), begin(items), end(items));
 | |
| 		_changed.fire({});
 | |
| 	} else {
 | |
| 		_items.insert(end(_items), begin(items), end(items));
 | |
| 		ranges::stable_sort(
 | |
| 			_items,
 | |
| 			ranges::greater(),
 | |
| 			&HistoryItem::id);
 | |
| 		ranges::unique(_items, ranges::greater(), &HistoryItem::id);
 | |
| 		_changed.fire({});
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void SavedSublist::setFullLoaded(bool loaded) {
 | |
| 	if (loaded != isFullLoaded()) {
 | |
| 		if (loaded) {
 | |
| 			_flags |= Flag::FullLoaded;
 | |
| 			if (_items.empty()) {
 | |
| 				updateChatListExistence();
 | |
| 			}
 | |
| 		} else {
 | |
| 			_flags &= ~Flag::FullLoaded;
 | |
| 		}
 | |
| 		_changed.fire({});
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int SavedSublist::fixedOnTopIndex() const {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| bool SavedSublist::shouldBeInChatList() const {
 | |
| 	return isPinnedDialog(FilterId()) || !_items.empty();
 | |
| }
 | |
| 
 | |
| Dialogs::UnreadState SavedSublist::chatListUnreadState() const {
 | |
| 	return {};
 | |
| }
 | |
| 
 | |
| Dialogs::BadgesState SavedSublist::chatListBadgesState() const {
 | |
| 	return {};
 | |
| }
 | |
| 
 | |
| HistoryItem *SavedSublist::chatListMessage() const {
 | |
| 	return _items.empty() ? nullptr : _items.front().get();
 | |
| }
 | |
| 
 | |
| bool SavedSublist::chatListMessageKnown() const {
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| const QString &SavedSublist::chatListName() const {
 | |
| 	return _history->chatListName();
 | |
| }
 | |
| 
 | |
| const base::flat_set<QString> &SavedSublist::chatListNameWords() const {
 | |
| 	return _history->chatListNameWords();
 | |
| }
 | |
| 
 | |
| const base::flat_set<QChar> &SavedSublist::chatListFirstLetters() const {
 | |
| 	return _history->chatListFirstLetters();
 | |
| }
 | |
| 
 | |
| const QString &SavedSublist::chatListNameSortKey() const {
 | |
| 	return _history->chatListNameSortKey();
 | |
| }
 | |
| 
 | |
| int SavedSublist::chatListNameVersion() const {
 | |
| 	return _history->chatListNameVersion();
 | |
| }
 | |
| 
 | |
| void SavedSublist::paintUserpic(
 | |
| 		Painter &p,
 | |
| 		Ui::PeerUserpicView &view,
 | |
| 		const Dialogs::Ui::PaintContext &context) const {
 | |
| 	_history->paintUserpic(p, view, context);
 | |
| }
 | |
| 
 | |
| void SavedSublist::chatListPreloadData() {
 | |
| 	peer()->loadUserpic();
 | |
| 	allowChatListMessageResolve();
 | |
| }
 | |
| 
 | |
| void SavedSublist::allowChatListMessageResolve() {
 | |
| 	if (_flags & Flag::ResolveChatListMessage) {
 | |
| 		return;
 | |
| 	}
 | |
| 	_flags |= Flag::ResolveChatListMessage;
 | |
| 	resolveChatListMessageGroup();
 | |
| }
 | |
| 
 | |
| bool SavedSublist::hasOrphanMediaGroupPart() const {
 | |
| 	if (isFullLoaded() || _items.size() != 1) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	return (_items.front()->groupId() != MessageGroupId());
 | |
| }
 | |
| 
 | |
| void SavedSublist::resolveChatListMessageGroup() {
 | |
| 	const auto item = chatListMessage();
 | |
| 	if (!(_flags & Flag::ResolveChatListMessage)
 | |
| 		|| !item
 | |
| 		|| !hasOrphanMediaGroupPart()) {
 | |
| 		return;
 | |
| 	}
 | |
| 	// If we set a single album part, request the full album.
 | |
| 	const auto withImages = !item->toPreview({
 | |
| 		.hideSender = true,
 | |
| 		.hideCaption = true }).images.empty();
 | |
| 	if (withImages) {
 | |
| 		owner().histories().requestGroupAround(item);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| } // namespace Data
 | 
