330 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
	
		
			8.8 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 "info/feed/info_feed_channels.h"
 | 
						|
 | 
						|
#include "info/feed/info_feed_channels_controllers.h"
 | 
						|
#include "info/profile/info_profile_icon.h"
 | 
						|
#include "info/profile/info_profile_button.h"
 | 
						|
#include "info/profile/info_profile_values.h"
 | 
						|
#include "info/channels/info_channels_widget.h"
 | 
						|
#include "info/info_controller.h"
 | 
						|
#include "info/info_memento.h"
 | 
						|
#include "ui/widgets/labels.h"
 | 
						|
#include "ui/widgets/buttons.h"
 | 
						|
#include "ui/widgets/scroll_area.h"
 | 
						|
#include "ui/wrap/padding_wrap.h"
 | 
						|
#include "ui/search_field_controller.h"
 | 
						|
#include "boxes/peer_list_controllers.h"
 | 
						|
#include "data/data_feed.h"
 | 
						|
#include "lang/lang_keys.h"
 | 
						|
#include "styles/style_boxes.h"
 | 
						|
#include "styles/style_info.h"
 | 
						|
 | 
						|
namespace Info {
 | 
						|
namespace FeedProfile {
 | 
						|
namespace {
 | 
						|
 | 
						|
constexpr auto kEnableSearchChannelsAfterCount = 20;
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
Channels::Channels(
 | 
						|
	QWidget *parent,
 | 
						|
	not_null<Controller*> controller)
 | 
						|
: RpWidget(parent)
 | 
						|
, _controller(controller)
 | 
						|
, _feed(_controller->key().feed())
 | 
						|
, _listController(std::make_unique<ChannelsController>(_controller)) {
 | 
						|
	setupHeader();
 | 
						|
	setupList();
 | 
						|
	setContent(_list.data());
 | 
						|
	_listController->setDelegate(static_cast<PeerListDelegate*>(this));
 | 
						|
 | 
						|
	_controller->searchFieldController()->queryValue(
 | 
						|
	) | rpl::start_with_next([this](QString &&query) {
 | 
						|
		peerListScrollToTop();
 | 
						|
		content()->searchQueryChanged(std::move(query));
 | 
						|
	}, lifetime());
 | 
						|
	Profile::FeedChannelsCountValue(
 | 
						|
		_feed
 | 
						|
	) | rpl::start_with_next([this](int count) {
 | 
						|
		const auto enabled = (count >= kEnableSearchChannelsAfterCount);
 | 
						|
		_controller->setSearchEnabledByContent(enabled);
 | 
						|
	}, lifetime());
 | 
						|
}
 | 
						|
 | 
						|
int Channels::desiredHeight() const {
 | 
						|
	auto desired = _header ? _header->height() : 0;
 | 
						|
	desired += st::infoChannelsList.item.height
 | 
						|
		* std::max(int(_feed->channels().size()), _list->fullRowsCount());
 | 
						|
	return qMax(height(), desired);
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<Ui::ScrollToRequest> Channels::scrollToRequests() const {
 | 
						|
	return _scrollToRequests.events();
 | 
						|
}
 | 
						|
 | 
						|
std::unique_ptr<ChannelsState> Channels::saveState() {
 | 
						|
	auto result = std::make_unique<ChannelsState>();
 | 
						|
	result->list = _listController->saveState();
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void Channels::restoreState(std::unique_ptr<ChannelsState> state) {
 | 
						|
	if (!state) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	_listController->restoreState(std::move(state->list));
 | 
						|
}
 | 
						|
 | 
						|
void Channels::setupHeader() {
 | 
						|
	if (_controller->section().type() == Section::Type::Channels) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	_header = object_ptr<Ui::FixedHeightWidget>(
 | 
						|
		this,
 | 
						|
		st::infoMembersHeader);
 | 
						|
	auto parent = _header.data();
 | 
						|
 | 
						|
	_openChannels = Ui::CreateChild<Profile::Button>(
 | 
						|
		parent,
 | 
						|
		rpl::single(QString()));
 | 
						|
 | 
						|
	// #feed
 | 
						|
	//object_ptr<Profile::FloatingIcon>(
 | 
						|
	//	parent,
 | 
						|
	//	st::infoIconFeed,
 | 
						|
	//	st::infoIconPosition);
 | 
						|
 | 
						|
	_titleWrap = Ui::CreateChild<Ui::RpWidget>(parent);
 | 
						|
	_title = setupTitle();
 | 
						|
	_addChannel = Ui::CreateChild<Ui::IconButton>(
 | 
						|
		_openChannels,
 | 
						|
		st::infoChannelsAddChannel);
 | 
						|
	_search = Ui::CreateChild<Ui::IconButton>(
 | 
						|
		_openChannels,
 | 
						|
		st::infoMembersSearch);
 | 
						|
 | 
						|
	setupButtons();
 | 
						|
 | 
						|
	widthValue(
 | 
						|
	) | rpl::start_with_next([this](int width) {
 | 
						|
		_header->resizeToWidth(width);
 | 
						|
	}, _header->lifetime());
 | 
						|
}
 | 
						|
 | 
						|
object_ptr<Ui::FlatLabel> Channels::setupTitle() {
 | 
						|
	auto result = object_ptr<Ui::FlatLabel>(
 | 
						|
		_titleWrap,
 | 
						|
		Profile::FeedChannelsCountValue(
 | 
						|
			_feed
 | 
						|
		) | rpl::map([](int count) {
 | 
						|
			return lng_feed_channels(lt_count, count);
 | 
						|
		}) | Profile::ToUpperValue(),
 | 
						|
		st::infoBlockHeaderLabel);
 | 
						|
	result->setAttribute(Qt::WA_TransparentForMouseEvents);
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void Channels::setupButtons() {
 | 
						|
	using namespace rpl::mappers;
 | 
						|
 | 
						|
	_openChannels->addClickHandler([this] {
 | 
						|
		showChannelsWithSearch(false);
 | 
						|
	});
 | 
						|
 | 
						|
	_addChannel->addClickHandler([this] { // TODO throttle(ripple duration)
 | 
						|
		this->addChannel();
 | 
						|
	});
 | 
						|
 | 
						|
	auto searchShown = Profile::FeedChannelsCountValue(_feed)
 | 
						|
		| rpl::map(_1 >= kEnableSearchChannelsAfterCount)
 | 
						|
		| rpl::distinct_until_changed()
 | 
						|
		| rpl::start_spawning(lifetime());
 | 
						|
	_search->showOn(rpl::duplicate(searchShown));
 | 
						|
	_search->addClickHandler([this] { // TODO throttle(ripple duration)
 | 
						|
		this->showChannelsWithSearch(true);
 | 
						|
	});
 | 
						|
 | 
						|
	std::move(
 | 
						|
		searchShown
 | 
						|
	) | rpl::start_with_next([this] {
 | 
						|
		updateHeaderControlsGeometry(width());
 | 
						|
	}, lifetime());
 | 
						|
}
 | 
						|
 | 
						|
void Channels::setupList() {
 | 
						|
	auto topSkip = _header ? _header->height() : 0;
 | 
						|
	_list = object_ptr<ListWidget>(
 | 
						|
		this,
 | 
						|
		_listController.get(),
 | 
						|
		st::infoChannelsList);
 | 
						|
	_list->scrollToRequests(
 | 
						|
	) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
 | 
						|
		auto addmin = (request.ymin < 0 || !_header)
 | 
						|
			? 0
 | 
						|
			: _header->height();
 | 
						|
		auto addmax = (request.ymax < 0 || !_header)
 | 
						|
			? 0
 | 
						|
			: _header->height();
 | 
						|
		_scrollToRequests.fire({
 | 
						|
			request.ymin + addmin,
 | 
						|
			request.ymax + addmax });
 | 
						|
	}, _list->lifetime());
 | 
						|
	widthValue(
 | 
						|
	) | rpl::start_with_next([this](int newWidth) {
 | 
						|
		_list->resizeToWidth(newWidth);
 | 
						|
	}, _list->lifetime());
 | 
						|
	_list->heightValue(
 | 
						|
	) | rpl::start_with_next([=](int listHeight) {
 | 
						|
		auto newHeight = (listHeight > st::membersMarginBottom)
 | 
						|
			? (topSkip
 | 
						|
				+ listHeight
 | 
						|
				+ st::membersMarginBottom)
 | 
						|
			: 0;
 | 
						|
		resize(width(), newHeight);
 | 
						|
	}, _list->lifetime());
 | 
						|
	_list->moveToLeft(0, topSkip);
 | 
						|
}
 | 
						|
 | 
						|
int Channels::resizeGetHeight(int newWidth) {
 | 
						|
	if (_header) {
 | 
						|
		updateHeaderControlsGeometry(newWidth);
 | 
						|
	}
 | 
						|
	return heightNoMargins();
 | 
						|
}
 | 
						|
 | 
						|
//void Channels::updateSearchEnabledByContent() {
 | 
						|
//	_controller->setSearchEnabledByContent(
 | 
						|
//		peerListFullRowsCount() >= kEnableSearchMembersAfterCount);
 | 
						|
//}
 | 
						|
 | 
						|
void Channels::updateHeaderControlsGeometry(int newWidth) {
 | 
						|
	_openChannels->setGeometry(0, st::infoProfileSkip, newWidth, st::infoMembersHeader - st::infoProfileSkip - st::infoMembersHeaderPaddingBottom);
 | 
						|
 | 
						|
	auto availableWidth = newWidth
 | 
						|
		- st::infoMembersButtonPosition.x();
 | 
						|
 | 
						|
	//auto cancelLeft = availableWidth - _cancelSearch->width();
 | 
						|
	//_cancelSearch->moveToLeft(
 | 
						|
	//	cancelLeft,
 | 
						|
	//	st::infoMembersButtonPosition.y());
 | 
						|
 | 
						|
	//auto searchShownLeft = st::infoIconPosition.x()
 | 
						|
	//	- st::infoMembersSearch.iconPosition.x();
 | 
						|
	//auto searchHiddenLeft = availableWidth - _search->width();
 | 
						|
	//auto searchShown = _searchShownAnimation.value(_searchShown ? 1. : 0.);
 | 
						|
	//auto searchCurrentLeft = anim::interpolate(
 | 
						|
	//	searchHiddenLeft,
 | 
						|
	//	searchShownLeft,
 | 
						|
	//	searchShown);
 | 
						|
	//_search->moveToLeft(
 | 
						|
	//	searchCurrentLeft,
 | 
						|
	//	st::infoMembersButtonPosition.y());
 | 
						|
 | 
						|
	//if (!_search->isHidden()) {
 | 
						|
	//	availableWidth -= st::infoMembersSearch.width;
 | 
						|
	//}
 | 
						|
	_addChannel->moveToLeft(
 | 
						|
		availableWidth - _addChannel->width(),
 | 
						|
		st::infoMembersButtonPosition.y(),
 | 
						|
		newWidth);
 | 
						|
	if (!_addChannel->isHidden()) {
 | 
						|
		availableWidth -= st::infoMembersSearch.width;
 | 
						|
	}
 | 
						|
	_search->moveToLeft(
 | 
						|
		availableWidth - _search->width(),
 | 
						|
		st::infoMembersButtonPosition.y(),
 | 
						|
		newWidth);
 | 
						|
 | 
						|
	//auto fieldLeft = anim::interpolate(
 | 
						|
	//	cancelLeft,
 | 
						|
	//	st::infoBlockHeaderPosition.x(),
 | 
						|
	//	searchShown);
 | 
						|
	//_searchField->setGeometryToLeft(
 | 
						|
	//	fieldLeft,
 | 
						|
	//	st::infoMembersSearchTop,
 | 
						|
	//	cancelLeft - fieldLeft,
 | 
						|
	//	_searchField->height());
 | 
						|
 | 
						|
	//_titleWrap->resize(
 | 
						|
	//	searchCurrentLeft - st::infoBlockHeaderPosition.x(),
 | 
						|
	//	_title->height());
 | 
						|
	_titleWrap->resize(
 | 
						|
		availableWidth - _addChannel->width() - st::infoBlockHeaderPosition.x(),
 | 
						|
		_title->height());
 | 
						|
	_titleWrap->moveToLeft(
 | 
						|
		st::infoBlockHeaderPosition.x(),
 | 
						|
		st::infoBlockHeaderPosition.y(),
 | 
						|
		newWidth);
 | 
						|
	_titleWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
 | 
						|
 | 
						|
	//_title->resizeToWidth(searchHiddenLeft);
 | 
						|
	_title->resizeToWidth(_titleWrap->width());
 | 
						|
	_title->moveToLeft(0, 0);
 | 
						|
}
 | 
						|
 | 
						|
void Channels::addChannel() {
 | 
						|
	EditController::Start(_feed);
 | 
						|
}
 | 
						|
 | 
						|
void Channels::showChannelsWithSearch(bool withSearch) {
 | 
						|
	auto contentMemento = std::make_unique<Info::Channels::Memento>(
 | 
						|
		_controller);
 | 
						|
	contentMemento->setState(saveState());
 | 
						|
	contentMemento->setSearchStartsFocused(withSearch);
 | 
						|
	auto mementoStack = std::vector<std::unique_ptr<ContentMemento>>();
 | 
						|
	mementoStack.push_back(std::move(contentMemento));
 | 
						|
	_controller->showSection(
 | 
						|
		Info::Memento(std::move(mementoStack)));
 | 
						|
}
 | 
						|
 | 
						|
void Channels::visibleTopBottomUpdated(
 | 
						|
		int visibleTop,
 | 
						|
		int visibleBottom) {
 | 
						|
	setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
 | 
						|
}
 | 
						|
 | 
						|
void Channels::peerListSetTitle(Fn<QString()> title) {
 | 
						|
}
 | 
						|
 | 
						|
void Channels::peerListSetAdditionalTitle(
 | 
						|
		Fn<QString()> title) {
 | 
						|
}
 | 
						|
 | 
						|
bool Channels::peerListIsRowSelected(not_null<PeerData*> peer) {
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
int Channels::peerListSelectedRowsCount() {
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
std::vector<not_null<PeerData*>> Channels::peerListCollectSelectedRows() {
 | 
						|
	return {};
 | 
						|
}
 | 
						|
 | 
						|
void Channels::peerListScrollToTop() {
 | 
						|
	_scrollToRequests.fire({ -1, -1 });
 | 
						|
}
 | 
						|
 | 
						|
void Channels::peerListAddSelectedRowInBunch(not_null<PeerData*> peer) {
 | 
						|
	Unexpected("Item selection in Info::Profile::Members.");
 | 
						|
}
 | 
						|
 | 
						|
void Channels::peerListFinishSelectedRowsBunch() {
 | 
						|
}
 | 
						|
 | 
						|
void Channels::peerListSetDescription(
 | 
						|
		object_ptr<Ui::FlatLabel> description) {
 | 
						|
	description.destroy();
 | 
						|
}
 | 
						|
 | 
						|
} // namespace FeedProfile
 | 
						|
} // namespace Info
 | 
						|
 |