295 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
	
		
			8.2 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_session.h"
 | 
						|
 | 
						|
#include "observer_peer.h"
 | 
						|
#include "history/history_item_components.h"
 | 
						|
#include "data/data_feed.h"
 | 
						|
 | 
						|
namespace Data {
 | 
						|
 | 
						|
Session::Session() {
 | 
						|
	Notify::PeerUpdateViewer(
 | 
						|
		Notify::PeerUpdate::Flag::UserIsContact
 | 
						|
	) | rpl::map([](const Notify::PeerUpdate &update) {
 | 
						|
		return update.peer->asUser();
 | 
						|
	}) | rpl::filter([](UserData *user) {
 | 
						|
		return user != nullptr;
 | 
						|
	}) | rpl::start_with_next([=](not_null<UserData*> user) {
 | 
						|
		userIsContactUpdated(user);
 | 
						|
	}, _lifetime);
 | 
						|
}
 | 
						|
 | 
						|
Session::~Session() = default;
 | 
						|
 | 
						|
void Session::markItemLayoutChanged(not_null<const HistoryItem*> item) {
 | 
						|
	_itemLayoutChanged.fire_copy(item);
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<not_null<const HistoryItem*>> Session::itemLayoutChanged() const {
 | 
						|
	return _itemLayoutChanged.events();
 | 
						|
}
 | 
						|
 | 
						|
void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
 | 
						|
	_itemRepaintRequest.fire_copy(item);
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const {
 | 
						|
	return _itemRepaintRequest.events();
 | 
						|
}
 | 
						|
 | 
						|
void Session::markItemRemoved(not_null<const HistoryItem*> item) {
 | 
						|
	_itemRemoved.fire_copy(item);
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<not_null<const HistoryItem*>> Session::itemRemoved() const {
 | 
						|
	return _itemRemoved.events();
 | 
						|
}
 | 
						|
 | 
						|
void Session::markHistoryUnloaded(not_null<const History*> history) {
 | 
						|
	_historyUnloaded.fire_copy(history);
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<not_null<const History*>> Session::historyUnloaded() const {
 | 
						|
	return _historyUnloaded.events();
 | 
						|
}
 | 
						|
 | 
						|
void Session::markHistoryCleared(not_null<const History*> history) {
 | 
						|
	_historyCleared.fire_copy(history);
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<not_null<const History*>> Session::historyCleared() const {
 | 
						|
	return _historyCleared.events();
 | 
						|
}
 | 
						|
 | 
						|
void Session::removeMegagroupParticipant(
 | 
						|
		not_null<ChannelData*> channel,
 | 
						|
		not_null<UserData*> user) {
 | 
						|
	_megagroupParticipantRemoved.fire({ channel, user });
 | 
						|
}
 | 
						|
 | 
						|
auto Session::megagroupParticipantRemoved() const
 | 
						|
-> rpl::producer<MegagroupParticipant> {
 | 
						|
	return _megagroupParticipantRemoved.events();
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<not_null<UserData*>> Session::megagroupParticipantRemoved(
 | 
						|
		not_null<ChannelData*> channel) const {
 | 
						|
	return megagroupParticipantRemoved(
 | 
						|
	) | rpl::filter([channel](auto updateChannel, auto user) {
 | 
						|
		return (updateChannel == channel);
 | 
						|
	}) | rpl::map([](auto updateChannel, auto user) {
 | 
						|
		return user;
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void Session::addNewMegagroupParticipant(
 | 
						|
		not_null<ChannelData*> channel,
 | 
						|
		not_null<UserData*> user) {
 | 
						|
	_megagroupParticipantAdded.fire({ channel, user });
 | 
						|
}
 | 
						|
 | 
						|
auto Session::megagroupParticipantAdded() const
 | 
						|
-> rpl::producer<MegagroupParticipant> {
 | 
						|
	return _megagroupParticipantAdded.events();
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<not_null<UserData*>> Session::megagroupParticipantAdded(
 | 
						|
		not_null<ChannelData*> channel) const {
 | 
						|
	return megagroupParticipantAdded(
 | 
						|
	) | rpl::filter([channel](auto updateChannel, auto user) {
 | 
						|
		return (updateChannel == channel);
 | 
						|
	}) | rpl::map([](auto updateChannel, auto user) {
 | 
						|
		return user;
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void Session::markStickersUpdated() {
 | 
						|
	_stickersUpdated.fire({});
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<> Session::stickersUpdated() const {
 | 
						|
	return _stickersUpdated.events();
 | 
						|
}
 | 
						|
 | 
						|
void Session::markSavedGifsUpdated() {
 | 
						|
	_savedGifsUpdated.fire({});
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<> Session::savedGifsUpdated() const {
 | 
						|
	return _savedGifsUpdated.events();
 | 
						|
}
 | 
						|
 | 
						|
void Session::userIsContactUpdated(not_null<UserData*> user) {
 | 
						|
	const auto &items = App::sharedContactItems();
 | 
						|
	const auto i = items.constFind(peerToUser(user->id));
 | 
						|
	if (i != items.cend()) {
 | 
						|
		for (const auto item : std::as_const(i.value())) {
 | 
						|
			item->setPendingInitDimensions();
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
HistoryItemsList Session::idsToItems(
 | 
						|
		const MessageIdsList &ids) const {
 | 
						|
	return ranges::view::all(
 | 
						|
		ids
 | 
						|
	) | ranges::view::transform([](const FullMsgId &fullId) {
 | 
						|
		return App::histItemById(fullId);
 | 
						|
	}) | ranges::view::filter([](HistoryItem *item) {
 | 
						|
		return item != nullptr;
 | 
						|
	}) | ranges::view::transform([](HistoryItem *item) {
 | 
						|
		return not_null<HistoryItem*>(item);
 | 
						|
	}) | ranges::to_vector;
 | 
						|
}
 | 
						|
 | 
						|
MessageIdsList Session::itemsToIds(
 | 
						|
		const HistoryItemsList &items) const {
 | 
						|
	return ranges::view::all(
 | 
						|
		items
 | 
						|
	) | ranges::view::transform([](not_null<HistoryItem*> item) {
 | 
						|
		return item->fullId();
 | 
						|
	}) | ranges::to_vector;
 | 
						|
}
 | 
						|
 | 
						|
MessageIdsList Session::groupToIds(
 | 
						|
		not_null<HistoryMessageGroup*> group) const {
 | 
						|
	auto result = itemsToIds(group->others);
 | 
						|
	result.push_back(group->leader->fullId());
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void Session::setPinnedDialog(const Dialogs::Key &key, bool pinned) {
 | 
						|
	setIsPinned(key, pinned);
 | 
						|
}
 | 
						|
 | 
						|
void Session::applyPinnedDialogs(const QVector<MTPDialog> &list) {
 | 
						|
	clearPinnedDialogs();
 | 
						|
	for (auto i = list.size(); i != 0;) {
 | 
						|
		const auto &dialog = list[--i];
 | 
						|
		switch (dialog.type()) {
 | 
						|
		case mtpc_dialog: {
 | 
						|
			const auto &dialogData = dialog.c_dialog();
 | 
						|
			if (const auto peer = peerFromMTP(dialogData.vpeer)) {
 | 
						|
				setPinnedDialog(App::history(peer), true);
 | 
						|
			}
 | 
						|
		} break;
 | 
						|
 | 
						|
		case mtpc_dialogFeed: {
 | 
						|
			const auto &feedData = dialog.c_dialogFeed();
 | 
						|
			const auto feedId = feedData.vfeed_id.v;
 | 
						|
			setPinnedDialog(feed(feedId), true);
 | 
						|
		} break;
 | 
						|
 | 
						|
		default: Unexpected("Type in ApiWrap::applyDialogsPinned.");
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::applyPinnedDialogs(const QVector<MTPDialogPeer> &list) {
 | 
						|
	clearPinnedDialogs();
 | 
						|
	for (auto i = list.size(); i != 0;) {
 | 
						|
		const auto &dialogPeer = list[--i];
 | 
						|
		switch (dialogPeer.type()) {
 | 
						|
		case mtpc_dialogPeer: {
 | 
						|
			const auto &peerData = dialogPeer.c_dialogPeer();
 | 
						|
			if (const auto peerId = peerFromMTP(peerData.vpeer)) {
 | 
						|
				setPinnedDialog(App::history(peerId), true);
 | 
						|
			}
 | 
						|
		} break;
 | 
						|
		case mtpc_dialogPeerFeed: {
 | 
						|
			const auto &feedData = dialogPeer.c_dialogPeerFeed();
 | 
						|
			const auto feedId = feedData.vfeed_id.v;
 | 
						|
			setPinnedDialog(feed(feedId), true);
 | 
						|
		} break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int Session::pinnedDialogsCount() const {
 | 
						|
	return _pinnedDialogs.size();
 | 
						|
}
 | 
						|
 | 
						|
const std::deque<Dialogs::Key> &Session::pinnedDialogsOrder() const {
 | 
						|
	return _pinnedDialogs;
 | 
						|
}
 | 
						|
 | 
						|
void Session::clearPinnedDialogs() {
 | 
						|
	while (!_pinnedDialogs.empty()) {
 | 
						|
		setPinnedDialog(_pinnedDialogs.back(), false);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::reorderTwoPinnedDialogs(
 | 
						|
		const Dialogs::Key &key1,
 | 
						|
		const Dialogs::Key &key2) {
 | 
						|
	const auto &order = pinnedDialogsOrder();
 | 
						|
	const auto index1 = ranges::find(order, key1) - begin(order);
 | 
						|
	const auto index2 = ranges::find(order, key2) - begin(order);
 | 
						|
	Assert(index1 >= 0 && index1 < order.size());
 | 
						|
	Assert(index2 >= 0 && index2 < order.size());
 | 
						|
	Assert(index1 != index2);
 | 
						|
	std::swap(_pinnedDialogs[index1], _pinnedDialogs[index2]);
 | 
						|
	key1.entry()->cachePinnedIndex(index2 + 1);
 | 
						|
	key2.entry()->cachePinnedIndex(index1 + 1);
 | 
						|
}
 | 
						|
 | 
						|
void Session::setIsPinned(const Dialogs::Key &key, bool pinned) {
 | 
						|
	const auto already = ranges::find(_pinnedDialogs, key);
 | 
						|
	if (pinned) {
 | 
						|
		if (already != end(_pinnedDialogs)) {
 | 
						|
			auto saved = std::move(*already);
 | 
						|
			const auto alreadyIndex = already - end(_pinnedDialogs);
 | 
						|
			const auto count = int(size(_pinnedDialogs));
 | 
						|
			Assert(alreadyIndex < count);
 | 
						|
			for (auto index = alreadyIndex + 1; index != count; ++index) {
 | 
						|
				_pinnedDialogs[index - 1] = std::move(_pinnedDialogs[index]);
 | 
						|
				_pinnedDialogs[index - 1].entry()->cachePinnedIndex(index);
 | 
						|
			}
 | 
						|
			_pinnedDialogs.back() = std::move(saved);
 | 
						|
			_pinnedDialogs.back().entry()->cachePinnedIndex(count);
 | 
						|
		} else {
 | 
						|
			_pinnedDialogs.push_back(key);
 | 
						|
			if (_pinnedDialogs.size() > Global::PinnedDialogsCountMax()) {
 | 
						|
				_pinnedDialogs.front().entry()->cachePinnedIndex(0);
 | 
						|
				_pinnedDialogs.pop_front();
 | 
						|
 | 
						|
				auto index = 0;
 | 
						|
				for (const auto &pinned : _pinnedDialogs) {
 | 
						|
					pinned.entry()->cachePinnedIndex(++index);
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				key.entry()->cachePinnedIndex(_pinnedDialogs.size());
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else if (!pinned && already != _pinnedDialogs.end()) {
 | 
						|
		key.entry()->cachePinnedIndex(0);
 | 
						|
		_pinnedDialogs.erase(already);
 | 
						|
		auto index = 0;
 | 
						|
		for (const auto &pinned : _pinnedDialogs) {
 | 
						|
			pinned.entry()->cachePinnedIndex(++index);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
not_null<Data::Feed*> Session::feed(FeedId id) {
 | 
						|
	if (const auto result = feedLoaded(id)) {
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
	const auto [it, ok] = _feeds.emplace(
 | 
						|
		id,
 | 
						|
		std::make_unique<Data::Feed>(id));
 | 
						|
	return it->second.get();
 | 
						|
}
 | 
						|
 | 
						|
Data::Feed *Session::feedLoaded(FeedId id) {
 | 
						|
	const auto it = _feeds.find(id);
 | 
						|
	return (it == _feeds.end()) ? nullptr : it->second.get();
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Data
 |