| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- /*
- 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/similar_peers/info_similar_peers_widget.h"
- #include "api/api_chat_participants.h"
- #include "apiwrap.h"
- #include "boxes/peer_list_box.h"
- #include "data/data_channel.h"
- #include "data/data_peer_values.h"
- #include "data/data_premium_limits.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "info/info_controller.h"
- #include "main/main_session.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/scroll_area.h"
- #include "ui/widgets/tooltip.h"
- #include "ui/ui_utility.h"
- #include "lang/lang_keys.h"
- #include "settings/settings_premium.h"
- #include "window/window_session_controller.h"
- #include "styles/style_info.h"
- #include "styles/style_widgets.h"
- namespace Info::SimilarPeers {
- namespace {
- class ListController final : public PeerListController {
- public:
- ListController(
- not_null<Controller*> controller,
- not_null<PeerData*> peer);
- Main::Session &session() const override;
- void prepare() override;
- void rowClicked(not_null<PeerListRow*> row) override;
- void loadMoreRows() override;
- std::unique_ptr<PeerListRow> createRestoredRow(
- not_null<PeerData*> peer) override {
- return createRow(peer);
- }
- std::unique_ptr<PeerListState> saveState() const override;
- void restoreState(std::unique_ptr<PeerListState> state) override;
- void setContentWidget(not_null<Ui::RpWidget*> widget);
- [[nodiscard]] rpl::producer<int> unlockHeightValue() const;
- private:
- std::unique_ptr<PeerListRow> createRow(not_null<PeerData*> peer);
- void setupUnlock();
- void rebuild();
- struct SavedState : SavedStateBase {
- };
- const not_null<Controller*> _controller;
- const not_null<PeerData*> _peer;
- Ui::RpWidget *_content = nullptr;
- Ui::RpWidget *_unlock = nullptr;
- rpl::variable<int> _unlockHeight;
- };
- ListController::ListController(
- not_null<Controller*> controller,
- not_null<PeerData*> peer)
- : PeerListController()
- , _controller(controller)
- , _peer(peer) {
- }
- Main::Session &ListController::session() const {
- return _peer->session();
- }
- std::unique_ptr<PeerListRow> ListController::createRow(
- not_null<PeerData*> peer) {
- auto result = std::make_unique<PeerListRow>(peer);
- if (const auto channel = peer->asChannel()) {
- if (const auto count = channel->membersCount(); count > 1) {
- result->setCustomStatus(
- tr::lng_chat_status_subscribers(
- tr::now,
- lt_count_decimal,
- count));
- }
- }
- return result;
- }
- void ListController::prepare() {
- delegate()->peerListSetTitle(_peer->isBroadcast()
- ? tr::lng_similar_channels_title()
- : tr::lng_similar_bots_title());
- const auto participants = &_peer->session().api().chatParticipants();
- Data::AmPremiumValue(
- &_peer->session()
- ) | rpl::start_with_next([=] {
- participants->loadSimilarPeers(_peer);
- rebuild();
- }, lifetime());
- participants->similarLoaded(
- ) | rpl::filter(
- rpl::mappers::_1 == _peer
- ) | rpl::start_with_next([=] {
- rebuild();
- }, lifetime());
- }
- void ListController::setContentWidget(not_null<Ui::RpWidget*> widget) {
- _content = widget;
- }
- rpl::producer<int> ListController::unlockHeightValue() const {
- return _unlockHeight.value();
- }
- void ListController::rebuild() {
- const auto participants = &_peer->session().api().chatParticipants();
- const auto &list = participants->similar(_peer);
- for (const auto peer : list.list) {
- if (!delegate()->peerListFindRow(peer->id.value)) {
- delegate()->peerListAppendRow(createRow(peer));
- }
- }
- if (!list.more
- || _peer->session().premium()
- || !_peer->session().premiumPossible()) {
- delete base::take(_unlock);
- _unlockHeight = 0;
- } else if (!_unlock) {
- setupUnlock();
- }
- delegate()->peerListRefreshRows();
- }
- void ListController::setupUnlock() {
- Expects(_content != nullptr);
- _unlock = Ui::CreateChild<Ui::RpWidget>(_content);
- _unlock->show();
- const auto button = ::Settings::CreateLockedButton(
- _unlock,
- (_peer->isBroadcast()
- ? tr::lng_similar_channels_show_more()
- : tr::lng_similar_bots_show_more()),
- st::similarChannelsLock,
- rpl::single(true));
- button->setClickedCallback([=] {
- const auto window = _controller->parentController();
- ::Settings::ShowPremium(window, u"similar_channels"_q);
- });
- const auto upto = Data::PremiumLimits(
- &_peer->session()).similarChannelsPremium();
- const auto about = Ui::CreateChild<Ui::FlatLabel>(
- _unlock,
- (_peer->isBroadcast()
- ? tr::lng_similar_channels_premium_all
- : tr::lng_similar_bots_premium_all)(
- lt_count,
- rpl::single(upto * 1.),
- lt_link,
- tr::lng_similar_channels_premium_all_link(
- ) | Ui::Text::ToBold() | Ui::Text::ToLink(),
- Ui::Text::RichLangValue),
- st::similarChannelsLockAbout);
- about->setClickHandlerFilter([=](const auto &...) {
- const auto window = _controller->parentController();
- ::Settings::ShowPremium(window, u"similar_channels"_q);
- return false;
- });
- rpl::combine(
- _content->sizeValue(),
- (_peer->isBroadcast()
- ? tr::lng_similar_channels_show_more()
- : tr::lng_similar_bots_show_more())
- ) | rpl::start_with_next([=](QSize size, const auto &) {
- auto top = st::similarChannelsLockFade
- + st::similarChannelsLockPadding.top();
- button->setGeometry(
- st::similarChannelsLockPadding.left(),
- top,
- (size.width()
- - st::similarChannelsLockPadding.left()
- - st::similarChannelsLockPadding.right()),
- button->height());
- top += button->height() + st::similarChannelsLockPadding.bottom();
- const auto minWidth = st::similarChannelsLockAbout.minWidth;
- const auto maxWidth = std::max(
- minWidth + 1,
- (size.width()
- - st::similarChannelsLockAboutPadding.left()
- - st::similarChannelsLockAboutPadding.right()));
- const auto countAboutHeight = [&](int width) {
- about->resizeToWidth(width);
- return about->height();
- };
- const auto desired = Ui::FindNiceTooltipWidth(
- minWidth,
- maxWidth,
- countAboutHeight);
- about->resizeToWidth(desired);
- about->move((size.width() - about->width()) / 2, top);
- top += about->height()
- + st::similarChannelsLockAboutPadding.bottom();
- _unlock->setGeometry(0, size.height() - top, size.width(), top);
- }, _unlock->lifetime());
- _unlockHeight = _unlock->heightValue();
- _unlock->paintRequest(
- ) | rpl::start_with_next([=] {
- auto p = QPainter(_unlock);
- const auto width = _unlock->width();
- const auto fade = st::similarChannelsLockFade;
- auto gradient = QLinearGradient(0, 0, 0, fade);
- gradient.setStops({
- { 0., QColor(255, 255, 255, 0) },
- { 1., st::windowBg->c },
- });
- p.fillRect(0, 0, width, fade, gradient);
- p.fillRect(0, fade, width, _unlock->height() - fade, st::windowBg);
- }, _unlock->lifetime());
- }
- void ListController::loadMoreRows() {
- }
- std::unique_ptr<PeerListState> ListController::saveState() const {
- auto result = PeerListController::saveState();
- auto my = std::make_unique<SavedState>();
- result->controllerState = std::move(my);
- return result;
- }
- void ListController::restoreState(
- std::unique_ptr<PeerListState> state) {
- auto typeErasedState = state
- ? state->controllerState.get()
- : nullptr;
- if (dynamic_cast<SavedState*>(typeErasedState)) {
- PeerListController::restoreState(std::move(state));
- }
- }
- void ListController::rowClicked(not_null<PeerListRow*> row) {
- _controller->parentController()->showPeerHistory(
- row->peer(),
- Window::SectionShow::Way::Forward);
- }
- } // namespace
- class InnerWidget final
- : public Ui::RpWidget
- , private PeerListContentDelegate {
- public:
- InnerWidget(
- QWidget *parent,
- not_null<Controller*> controller,
- not_null<PeerData*> peer);
- [[nodiscard]] not_null<PeerData*> peer() const {
- return _peer;
- }
- rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
- int desiredHeight() const;
- void saveState(not_null<Memento*> memento);
- void restoreState(not_null<Memento*> memento);
- protected:
- void visibleTopBottomUpdated(
- int visibleTop,
- int visibleBottom) override;
- private:
- using ListWidget = PeerListContent;
- // PeerListContentDelegate interface.
- void peerListSetTitle(rpl::producer<QString> title) override;
- void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
- bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
- int peerListSelectedRowsCount() override;
- void peerListScrollToTop() override;
- void peerListAddSelectedPeerInBunch(
- not_null<PeerData*> peer) override;
- void peerListAddSelectedRowInBunch(
- not_null<PeerListRow*> row) override;
- void peerListFinishSelectedRowsBunch() override;
- void peerListSetDescription(
- object_ptr<Ui::FlatLabel> description) override;
- std::shared_ptr<Main::SessionShow> peerListUiShow() override;
- object_ptr<ListWidget> setupList(
- RpWidget *parent,
- not_null<ListController*> controller);
- const std::shared_ptr<Main::SessionShow> _show;
- not_null<Controller*> _controller;
- const not_null<PeerData*> _peer;
- std::unique_ptr<ListController> _listController;
- object_ptr<ListWidget> _list;
- rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
- };
- InnerWidget::InnerWidget(
- QWidget *parent,
- not_null<Controller*> controller,
- not_null<PeerData*> peer)
- : RpWidget(parent)
- , _show(controller->uiShow())
- , _controller(controller)
- , _peer(peer)
- , _listController(std::make_unique<ListController>(controller, _peer))
- , _list(setupList(this, _listController.get())) {
- setContent(_list.data());
- _listController->setDelegate(static_cast<PeerListDelegate*>(this));
- }
- void InnerWidget::visibleTopBottomUpdated(
- int visibleTop,
- int visibleBottom) {
- setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
- }
- void InnerWidget::saveState(not_null<Memento*> memento) {
- memento->setListState(_listController->saveState());
- }
- void InnerWidget::restoreState(not_null<Memento*> memento) {
- _listController->restoreState(memento->listState());
- }
- rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
- return _scrollToRequests.events();
- }
- int InnerWidget::desiredHeight() const {
- auto desired = 0;
- desired += _list->fullRowsCount() * st::infoMembersList.item.height;
- return qMax(height(), desired);
- }
- object_ptr<InnerWidget::ListWidget> InnerWidget::setupList(
- RpWidget *parent,
- not_null<ListController*> controller) {
- controller->setStyleOverrides(&st::infoMembersList);
- auto result = object_ptr<ListWidget>(
- parent,
- controller);
- controller->setContentWidget(this);
- result->scrollToRequests(
- ) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
- auto addmin = (request.ymin < 0)
- ? 0
- : st::infoCommonGroupsMargin.top();
- auto addmax = (request.ymax < 0)
- ? 0
- : st::infoCommonGroupsMargin.top();
- _scrollToRequests.fire({
- request.ymin + addmin,
- request.ymax + addmax });
- }, result->lifetime());
- result->moveToLeft(0, st::infoCommonGroupsMargin.top());
- parent->widthValue(
- ) | rpl::start_with_next([list = result.data()](int newWidth) {
- list->resizeToWidth(newWidth);
- }, result->lifetime());
- rpl::combine(
- result->heightValue(),
- controller->unlockHeightValue()
- ) | rpl::start_with_next([=](int listHeight, int unlockHeight) {
- auto newHeight = st::infoCommonGroupsMargin.top()
- + listHeight
- + (unlockHeight
- ? (unlockHeight - st::similarChannelsLockOverlap)
- : st::infoCommonGroupsMargin.bottom());
- parent->resize(parent->width(), std::max(newHeight, 0));
- }, result->lifetime());
- return result;
- }
- void InnerWidget::peerListSetTitle(rpl::producer<QString> title) {
- }
- void InnerWidget::peerListSetAdditionalTitle(rpl::producer<QString> title) {
- }
- bool InnerWidget::peerListIsRowChecked(not_null<PeerListRow*> row) {
- return false;
- }
- int InnerWidget::peerListSelectedRowsCount() {
- return 0;
- }
- void InnerWidget::peerListScrollToTop() {
- _scrollToRequests.fire({ -1, -1 });
- }
- void InnerWidget::peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) {
- Unexpected("Item selection in Info::Profile::Members.");
- }
- void InnerWidget::peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) {
- Unexpected("Item selection in Info::Profile::Members.");
- }
- void InnerWidget::peerListFinishSelectedRowsBunch() {
- }
- void InnerWidget::peerListSetDescription(
- object_ptr<Ui::FlatLabel> description) {
- description.destroy();
- }
- std::shared_ptr<Main::SessionShow> InnerWidget::peerListUiShow() {
- return _show;
- }
- Memento::Memento(not_null<PeerData*> peer)
- : ContentMemento(peer, nullptr, PeerId()) {
- }
- Section Memento::section() const {
- return Section(Section::Type::SimilarPeers);
- }
- object_ptr<ContentWidget> Memento::createWidget(
- QWidget *parent,
- not_null<Controller*> controller,
- const QRect &geometry) {
- auto result = object_ptr<Widget>(parent, controller, peer());
- result->setInternalState(geometry, this);
- return result;
- }
- void Memento::setListState(std::unique_ptr<PeerListState> state) {
- _listState = std::move(state);
- }
- std::unique_ptr<PeerListState> Memento::listState() {
- return std::move(_listState);
- }
- Memento::~Memento() = default;
- Widget::Widget(
- QWidget *parent,
- not_null<Controller*> controller,
- not_null<PeerData*> peer)
- : ContentWidget(parent, controller) {
- _inner = setInnerWidget(object_ptr<InnerWidget>(
- this,
- controller,
- peer));
- }
- rpl::producer<QString> Widget::title() {
- return peer()->isBroadcast()
- ? tr::lng_similar_channels_title()
- : tr::lng_similar_bots_title();
- }
- not_null<PeerData*> Widget::peer() const {
- return _inner->peer();
- }
- bool Widget::showInternal(not_null<ContentMemento*> memento) {
- if (!controller()->validateMementoPeer(memento)) {
- return false;
- }
- if (auto similarMemento = dynamic_cast<Memento*>(memento.get())) {
- if (similarMemento->peer() == peer()) {
- restoreState(similarMemento);
- return true;
- }
- }
- return false;
- }
- void Widget::setInternalState(
- const QRect &geometry,
- not_null<Memento*> memento) {
- setGeometry(geometry);
- Ui::SendPendingMoveResizeEvents(this);
- restoreState(memento);
- }
- std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
- auto result = std::make_shared<Memento>(peer());
- saveState(result.get());
- return result;
- }
- void Widget::saveState(not_null<Memento*> memento) {
- memento->setScrollTop(scrollTopSave());
- _inner->saveState(memento);
- }
- void Widget::restoreState(not_null<Memento*> memento) {
- _inner->restoreState(memento);
- scrollTopRestore(memento->scrollTop());
- }
- } // namespace Info::SimilarPeers
|