2018-01-22 19:51:38 +00:00
|
|
|
/*
|
|
|
|
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"
|
|
|
|
|
2018-02-07 12:40:37 +00:00
|
|
|
#include "info/feed/info_feed_channels_controllers.h"
|
2018-01-22 19:51:38 +00:00
|
|
|
#include "info/profile/info_profile_icon.h"
|
|
|
|
#include "info/profile/info_profile_button.h"
|
2018-02-07 12:40:37 +00:00
|
|
|
#include "info/profile/info_profile_values.h"
|
2018-02-22 17:38:00 +00:00
|
|
|
#include "info/channels/info_channels_widget.h"
|
2018-01-22 19:51:38 +00:00
|
|
|
#include "info/info_controller.h"
|
2018-02-22 17:38:00 +00:00
|
|
|
#include "info/info_memento.h"
|
2018-01-22 19:51:38 +00:00
|
|
|
#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"
|
2018-02-08 09:20:14 +00:00
|
|
|
#include "data/data_feed.h"
|
2018-02-07 12:40:37 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2018-01-22 19:51:38 +00:00
|
|
|
#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())
|
2018-02-07 12:40:37 +00:00
|
|
|
, _listController(std::make_unique<ChannelsController>(_controller)) {
|
2018-01-22 19:51:38 +00:00
|
|
|
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());
|
2018-02-07 12:40:37 +00:00
|
|
|
Profile::FeedChannelsCountValue(
|
|
|
|
_feed
|
|
|
|
) | rpl::start_with_next([this](int count) {
|
|
|
|
const auto enabled = (count >= kEnableSearchChannelsAfterCount);
|
|
|
|
_controller->setSearchEnabledByContent(enabled);
|
|
|
|
}, lifetime());
|
2018-01-22 19:51:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int Channels::desiredHeight() const {
|
|
|
|
auto desired = _header ? _header->height() : 0;
|
2018-02-22 17:38:00 +00:00
|
|
|
desired += st::infoChannelsList.item.height
|
2018-02-08 09:20:14 +00:00
|
|
|
* std::max(int(_feed->channels().size()), _list->fullRowsCount());
|
2018-01-22 19:51:38 +00:00
|
|
|
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() {
|
2018-02-22 17:38:00 +00:00
|
|
|
if (_controller->section().type() == Section::Type::Channels) {
|
2018-01-22 19:51:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
_header = object_ptr<Ui::FixedHeightWidget>(
|
|
|
|
this,
|
|
|
|
st::infoMembersHeader);
|
|
|
|
auto parent = _header.data();
|
|
|
|
|
|
|
|
_openChannels = Ui::CreateChild<Profile::Button>(
|
|
|
|
parent,
|
|
|
|
rpl::single(QString()));
|
|
|
|
|
2018-10-15 09:37:01 +00:00
|
|
|
// #feed
|
|
|
|
//object_ptr<Profile::FloatingIcon>(
|
|
|
|
// parent,
|
|
|
|
// st::infoIconFeed,
|
|
|
|
// st::infoIconPosition);
|
2018-01-22 19:51:38 +00:00
|
|
|
|
|
|
|
_titleWrap = Ui::CreateChild<Ui::RpWidget>(parent);
|
|
|
|
_title = setupTitle();
|
|
|
|
_addChannel = Ui::CreateChild<Ui::IconButton>(
|
|
|
|
_openChannels,
|
2018-02-22 13:50:56 +00:00
|
|
|
st::infoChannelsAddChannel);
|
2018-01-22 19:51:38 +00:00
|
|
|
_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,
|
2018-02-07 12:40:37 +00:00
|
|
|
Profile::FeedChannelsCountValue(
|
|
|
|
_feed
|
|
|
|
) | rpl::map([](int count) {
|
|
|
|
return lng_feed_channels(lt_count, count);
|
|
|
|
}) | Profile::ToUpperValue(),
|
2018-01-22 19:51:38 +00:00
|
|
|
st::infoBlockHeaderLabel);
|
|
|
|
result->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Channels::setupButtons() {
|
|
|
|
using namespace rpl::mappers;
|
|
|
|
|
|
|
|
_openChannels->addClickHandler([this] {
|
|
|
|
showChannelsWithSearch(false);
|
|
|
|
});
|
|
|
|
|
2018-02-22 17:38:00 +00:00
|
|
|
_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());
|
2018-01-22 19:51:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Channels::setupList() {
|
|
|
|
auto topSkip = _header ? _header->height() : 0;
|
|
|
|
_list = object_ptr<ListWidget>(
|
|
|
|
this,
|
|
|
|
_listController.get(),
|
2018-02-22 17:38:00 +00:00
|
|
|
st::infoChannelsList);
|
2018-01-22 19:51:38 +00:00
|
|
|
_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();
|
2019-04-02 09:13:30 +00:00
|
|
|
//auto searchShown = _searchShownAnimation.value(_searchShown ? 1. : 0.);
|
2018-01-22 19:51:38 +00:00
|
|
|
//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() {
|
2018-02-22 17:38:00 +00:00
|
|
|
EditController::Start(_feed);
|
2018-01-22 19:51:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Channels::showChannelsWithSearch(bool withSearch) {
|
2018-02-22 17:38:00 +00:00
|
|
|
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)));
|
2018-01-22 19:51:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Channels::visibleTopBottomUpdated(
|
|
|
|
int visibleTop,
|
|
|
|
int visibleBottom) {
|
|
|
|
setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
|
|
|
|
}
|
|
|
|
|
2018-06-04 15:35:11 +00:00
|
|
|
void Channels::peerListSetTitle(Fn<QString()> title) {
|
2018-01-22 19:51:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Channels::peerListSetAdditionalTitle(
|
2018-06-04 15:35:11 +00:00
|
|
|
Fn<QString()> title) {
|
2018-01-22 19:51:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|