/* 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/community/info_community_widget.h" #include "boxes/peers/community_box.h" #include "data/data_changes.h" #include "data/data_channel.h" #include "data/data_community.h" #include "info/profile/info_profile_top_bar.h" #include "info/profile/info_profile_values.h" #include "info/info_controller.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/text/text_utilities.h" #include "ui/wrap/vertical_layout.h" #include "ui/rp_widget.h" #include "ui/ui_utility.h" #include "styles/style_info.h" namespace Info::Community { class InnerWidget final : public Ui::VerticalLayout { public: InnerWidget( QWidget *parent, not_null controller, not_null peer); [[nodiscard]] not_null peer() const { return _peer; } [[nodiscard]] rpl::producer<> backRequest() const; void enableBackButton(); void showFinished(); [[nodiscard]] bool hasFlexibleTopBar() const; base::weak_qptr createPinnedToTop( not_null parent); private: [[nodiscard]] rpl::producer chatsStatusValue() const; const not_null _controller; const not_null _peer; rpl::variable _backToggles; rpl::event_stream<> _backClicks; rpl::event_stream<> _showFinished; }; InnerWidget::InnerWidget( QWidget *parent, not_null controller, not_null peer) : Ui::VerticalLayout(parent) , _controller(controller) , _peer(peer) { if (const auto community = peer->asChannel()) { SetupCommunityContent( this, controller->parentController(), community, controller->uiShow()); } } rpl::producer<> InnerWidget::backRequest() const { return _backClicks.events(); } void InnerWidget::enableBackButton() { _backToggles.force_assign(true); } void InnerWidget::showFinished() { _showFinished.fire({}); } bool InnerWidget::hasFlexibleTopBar() const { const auto channel = _peer->asChannel(); return channel && channel->isCommunity(); } rpl::producer InnerWidget::chatsStatusValue() const { const auto channel = _peer->asChannel(); Assert(channel != nullptr); return channel->session().changes().peerFlagsValue( channel, Data::PeerUpdate::Flag::FullInfo ) | rpl::map([=] { return channel->communityInfo(); }) | rpl::map([](Data::CommunityInfo *info) -> rpl::producer { if (!info) { return rpl::single(0); } return info->linkedPeersValue() | rpl::map([info] { return int(info->linkedPeers().size()); }); }) | rpl::flatten_latest() | rpl::map([](int count) { return TextWithEntities{ tr::lng_community_chats(tr::now, lt_count, float64(count)), }; }); } base::weak_qptr InnerWidget::createPinnedToTop( not_null parent) { if (!hasFlexibleTopBar()) { return nullptr; } const auto content = Ui::CreateChild( parent, Profile::TopBar::Descriptor{ .controller = _controller->parentController(), .key = _controller->key(), .wrap = _controller->wrapValue(), .source = Profile::TopBar::Source::Community, .peer = _peer, .backToggles = _backToggles.value(), .showFinished = _showFinished.events(), .customStatus = chatsStatusValue(), }); content->backRequest( ) | rpl::start_to_stream(_backClicks, content->lifetime()); return base::make_weak(not_null{ content }); } Memento::Memento(not_null peer) : ContentMemento(peer, nullptr, nullptr, PeerId()) { } Section Memento::section() const { return Section(Section::Type::Community); } object_ptr Memento::createWidget( QWidget *parent, not_null controller, const QRect &geometry) { auto result = object_ptr(parent, controller, peer()); result->setInternalState(geometry, this); return result; } Memento::~Memento() = default; Widget::Widget( QWidget *parent, not_null controller, not_null peer) : ContentWidget(parent, controller) , _inner( setupFlexibleInnerWidget( object_ptr(this, controller, peer), _flexibleScroll)) , _pinnedToTop(_inner->createPinnedToTop(this)) { _inner->move(0, 0); _inner->backRequest() | rpl::on_next([=] { checkBeforeClose([=] { controller->showBackFromStack(); }); }, _inner->lifetime()); if (_pinnedToTop) { _inner->widthValue( ) | rpl::on_next([=](int w) { _pinnedToTop->resizeToWidth(w); setScrollTopSkip(_pinnedToTop->height()); }, _pinnedToTop->lifetime()); _pinnedToTop->heightValue( ) | rpl::on_next([=](int h) { setScrollTopSkip(h); }, _pinnedToTop->lifetime()); } if (_pinnedToTop && _pinnedToTop->minimumHeight() && _inner->hasFlexibleTopBar()) { _flexibleScrollHelper = std::make_unique( scroll(), _inner, _pinnedToTop.get(), [=](QMargins margins) { ContentWidget::setPaintPadding(std::move(margins)); }, [=](rpl::producer> &&events) { ContentWidget::setViewport(std::move(events)); }, _flexibleScroll); } } void Widget::enableBackButton() { _inner->enableBackButton(); } void Widget::showFinished() { _inner->showFinished(); } rpl::producer Widget::title() { return Info::Profile::NameValue(peer()); } not_null Widget::peer() const { return _inner->peer(); } bool Widget::showInternal(not_null memento) { if (!controller()->validateMementoPeer(memento)) { return false; } if (auto communityMemento = dynamic_cast(memento.get())) { restoreState(communityMemento); return true; } return false; } void Widget::setInternalState( const QRect &geometry, not_null memento) { setGeometry(geometry); Ui::SendPendingMoveResizeEvents(this); restoreState(memento); } std::shared_ptr Widget::doCreateMemento() { auto result = std::make_shared(peer()); saveState(result.get()); return result; } void Widget::saveState(not_null memento) { memento->setScrollTop(scrollTopSave()); } void Widget::restoreState(not_null memento) { scrollTopRestore(memento->scrollTop()); } } // namespace Info::Community