From a47981054fd2348801b6523ded316d4e1af03e4e Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 22 Jan 2018 22:51:38 +0300 Subject: [PATCH] Feed info profile placeholder. --- Telegram/Resources/langs/lang.strings | 2 + .../SourceFiles/history/history_widget.cpp | 10 - Telegram/SourceFiles/history/history_widget.h | 2 - .../view/history_view_top_bar_widget.cpp | 11 +- .../info/feed/info_feed_channels.cpp | 352 ++++++++++++++++++ .../info/feed/info_feed_channels.h | 103 +++++ .../SourceFiles/info/feed/info_feed_cover.cpp | 191 ++++++++++ .../SourceFiles/info/feed/info_feed_cover.h | 64 ++++ .../feed/info_feed_profile_inner_widget.cpp | 115 ++++++ .../feed/info_feed_profile_inner_widget.h | 81 ++++ .../info/feed/info_feed_profile_widget.cpp | 116 ++++++ .../info/feed/info_feed_profile_widget.h | 69 ++++ .../SourceFiles/info/info_content_widget.cpp | 1 - .../SourceFiles/info/info_content_widget.h | 11 + Telegram/SourceFiles/info/info_controller.cpp | 48 ++- Telegram/SourceFiles/info/info_controller.h | 29 +- Telegram/SourceFiles/info/info_memento.cpp | 42 ++- Telegram/SourceFiles/info/info_memento.h | 17 +- Telegram/SourceFiles/info/info_top_bar.cpp | 8 +- Telegram/SourceFiles/info/info_top_bar.h | 3 +- .../SourceFiles/info/info_wrap_widget.cpp | 72 +++- Telegram/SourceFiles/info/info_wrap_widget.h | 10 +- .../info/media/info_media_inner_widget.cpp | 6 +- .../info/media/info_media_list_widget.cpp | 2 +- .../info/members/info_members_widget.cpp | 7 +- .../info/members/info_members_widget.h | 4 +- .../info/profile/info_profile_cover.cpp | 5 +- .../info/profile/info_profile_cover.h | 3 +- .../profile/info_profile_inner_widget.cpp | 9 +- .../info/profile/info_profile_members.cpp | 5 +- .../info/profile/info_profile_members.h | 4 +- .../info/profile/info_profile_widget.cpp | 4 + .../info/profile/info_profile_widget.h | 5 +- Telegram/SourceFiles/mainwidget.cpp | 15 +- .../media/player/media_player_panel.cpp | 4 +- .../media/player/media_player_panel.h | 2 +- Telegram/gyp/telegram_sources.txt | 8 + 37 files changed, 1336 insertions(+), 104 deletions(-) create mode 100644 Telegram/SourceFiles/info/feed/info_feed_channels.cpp create mode 100644 Telegram/SourceFiles/info/feed/info_feed_channels.h create mode 100644 Telegram/SourceFiles/info/feed/info_feed_cover.cpp create mode 100644 Telegram/SourceFiles/info/feed/info_feed_cover.h create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_widget.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index c04012cf2..1fb865d08 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1418,6 +1418,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_feed_name" = "Feed"; "lng_feed_show_next" = "Show Next"; +"lng_info_feed_title" = "Feed Info"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index e555633d0..552baa33b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3830,16 +3830,6 @@ void HistoryWidget::pushTabbedSelectorToThirdSection( destroyingPanel.destroy(); } -void HistoryWidget::pushInfoToThirdSection( - const Window::SectionShow ¶ms) { - if (!_peer) { - return; - } - controller()->showSection( - Info::Memento::Default(_peer), - params.withThirdColumn()); -} - void HistoryWidget::toggleTabbedSelectorMode() { if (_tabbedPanel) { if (controller()->canShowThirdSection() diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 11f6d2d25..494ac84e0 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -208,8 +208,6 @@ public: QRect historyRect() const; void pushTabbedSelectorToThirdSection( const Window::SectionShow ¶ms); - void pushInfoToThirdSection( - const Window::SectionShow ¶ms); void updateRecentStickers(); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 02af8d2b8..7a418d831 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -198,21 +198,20 @@ void TopBarWidget::toggleInfoSection() { && (Auth().settings().thirdSectionInfoEnabled() || Auth().settings().tabbedReplacedWithInfo())) { _controller->closeThirdSection(); - } else if (_activeChat.peer()) { - // #TODO feeds profile + } else if (_activeChat) { if (_controller->canShowThirdSection()) { Auth().settings().setThirdSectionInfoEnabled(true); Auth().saveSettingsDelayed(); if (Adaptive::ThreeColumn()) { _controller->showSection( - Info::Memento::Default(_activeChat.peer()), + Info::Memento::Default(_activeChat), Window::SectionShow().withThirdColumn()); } else { _controller->resizeForThirdSection(); _controller->updateColumnLayout(); } } else { - _controller->showSection(Info::Memento(_activeChat.peer()->id)); + infoClicked(); } } else { updateControlsVisibility(); @@ -345,7 +344,9 @@ void TopBarWidget::infoClicked() { if (!_activeChat) { return; } else if (const auto feed = _activeChat.feed()) { - // #TODO feeds profile + _controller->showSection(Info::Memento( + feed, + Info::Section(Info::Section::Type::Profile))); } else if (_activeChat.peer()->isSelf()) { _controller->showSection(Info::Memento( _activeChat.peer()->id, diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels.cpp b/Telegram/SourceFiles/info/feed/info_feed_channels.cpp new file mode 100644 index 000000000..a03b93c77 --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_channels.cpp @@ -0,0 +1,352 @@ +/* +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/profile/info_profile_icon.h" +#include "info/profile/info_profile_button.h" +#include "info/info_controller.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 "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) +: RpWidget(parent) +, _controller(controller) +, _feed(_controller->key().feed()) +, _listController(std::make_unique()) { + setupHeader(); + setupList(); + setContent(_list.data()); + _listController->setDelegate(static_cast(this)); + + _controller->searchFieldController()->queryValue( + ) | rpl::start_with_next([this](QString &&query) { + peerListScrollToTop(); + content()->searchQueryChanged(std::move(query)); + }, lifetime()); + //MembersCountValue( + // _peer + //) | 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; + //auto count = [this] { + // if (auto chat = _peer->asChat()) { + // return chat->count; + // } else if (auto channel = _peer->asChannel()) { + // return channel->membersCount(); + // } + // return 0; + //}(); + //desired += qMax(count, _list->fullRowsCount()) + // * st::infoMembersList.item.height; + return qMax(height(), desired); +} + +rpl::producer Channels::scrollToRequests() const { + return _scrollToRequests.events(); +} + +std::unique_ptr Channels::saveState() { + auto result = std::make_unique(); + result->list = _listController->saveState(); + return result; +} + +void Channels::restoreState(std::unique_ptr state) { + if (!state) { + return; + } + _listController->restoreState(std::move(state->list)); +} + +void Channels::setupHeader() { + if (_controller->section().type() == Section::Type::Members) { + return; + } + _header = object_ptr( + this, + st::infoMembersHeader); + auto parent = _header.data(); + + _openChannels = Ui::CreateChild( + parent, + rpl::single(QString())); + + object_ptr( + parent, + st::infoIconMembers, + st::infoIconPosition); + + _titleWrap = Ui::CreateChild(parent); + _title = setupTitle(); + _addChannel = Ui::CreateChild( + _openChannels, + st::infoMembersAddMember); + _search = Ui::CreateChild( + _openChannels, + st::infoMembersSearch); + + setupButtons(); + + widthValue( + ) | rpl::start_with_next([this](int width) { + _header->resizeToWidth(width); + }, _header->lifetime()); +} + +object_ptr Channels::setupTitle() { + auto result = object_ptr( + _titleWrap, + rpl::single(QString("Contacts")), + //MembersCountValue( + // _peer + //) | rpl::map([](int count) { + // return lng_chat_status_members(lt_count, count); + //}) | ToUpperValue(), + st::infoBlockHeaderLabel); + result->setAttribute(Qt::WA_TransparentForMouseEvents); + return result; +} + +void Channels::setupButtons() { + using namespace rpl::mappers; + + _openChannels->addClickHandler([this] { + showChannelsWithSearch(false); + }); + + //auto addMemberShown = CanAddMemberValue(_peer) + // | rpl::start_spawning(lifetime()); + //_addChannel->showOn(rpl::duplicate(addMemberShown)); + //_addChannel->addClickHandler([this] { // TODO throttle(ripple duration) + // this->addMember(); + //}); + + //auto searchShown = MembersCountValue(_peer) + // | rpl::map(_1 >= kEnableSearchMembersAfterCount) + // | rpl::distinct_until_changed() + // | rpl::start_spawning(lifetime()); + //_search->showOn(rpl::duplicate(searchShown)); + //_search->addClickHandler([this] { // TODO throttle(ripple duration) + // this->showMembersWithSearch(true); + //}); + + //rpl::combine( + // std::move(addMemberShown), + // std::move(searchShown) + //) | rpl::start_with_next([this] { + // updateHeaderControlsGeometry(width()); + //}, lifetime()); +} + +void Channels::setupList() { + auto topSkip = _header ? _header->height() : 0; + _list = object_ptr( + this, + _listController.get(), + st::infoMembersList); + _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.current(_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() { + //if (const auto chat = _peer->asChat()) { + // if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) { + // Ui::show(Box(chat)); + // } else { + // AddParticipantsBoxController::Start(chat); + // } + //} else if (const auto channel = _peer->asChannel()) { + // const auto state = _listController->saveState(); + // const auto users = ranges::view::all( + // state->list + // ) | ranges::view::transform([](not_null peer) { + // return peer->asUser(); + // }) | ranges::to_vector; + // AddParticipantsBoxController::Start( + // channel, + // { users.begin(), users.end() }); + //} +} + +void Channels::showChannelsWithSearch(bool withSearch) { + //auto contentMemento = std::make_unique( + // _controller); + //contentMemento->setState(saveState()); + //contentMemento->setSearchStartsFocused(withSearch); + //auto mementoStack = std::vector>(); + //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(base::lambda title) { +} + +void Channels::peerListSetAdditionalTitle( + base::lambda title) { +} + +bool Channels::peerListIsRowSelected(not_null peer) { + return false; +} + +int Channels::peerListSelectedRowsCount() { + return 0; +} + +std::vector> Channels::peerListCollectSelectedRows() { + return {}; +} + +void Channels::peerListScrollToTop() { + _scrollToRequests.fire({ -1, -1 }); +} + +void Channels::peerListAddSelectedRowInBunch(not_null peer) { + Unexpected("Item selection in Info::Profile::Members."); +} + +void Channels::peerListFinishSelectedRowsBunch() { +} + +void Channels::peerListSetDescription( + object_ptr description) { + description.destroy(); +} + +} // namespace FeedProfile +} // namespace Info + diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels.h b/Telegram/SourceFiles/info/feed/info_feed_channels.h new file mode 100644 index 000000000..013df7ced --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_channels.h @@ -0,0 +1,103 @@ +/* +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 +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "boxes/peer_list_box.h" + +namespace Ui { +class InputField; +class CrossButton; +class IconButton; +class FlatLabel; +struct ScrollToRequest; +class AbstractButton; +} // namespace Ui + +namespace Info { + +class Controller; +enum class Wrap; + +namespace Profile { +class Button; +} // namespace Profile + +namespace FeedProfile { + +class Memento; +struct ChannelsState { + std::unique_ptr list; + base::optional search; +}; + +class Channels + : public Ui::RpWidget + , private PeerListContentDelegate { +public: + Channels( + QWidget *parent, + not_null controller); + + rpl::producer scrollToRequests() const; + + std::unique_ptr saveState(); + void restoreState(std::unique_ptr state); + + int desiredHeight() const; + +protected: + void visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) override; + int resizeGetHeight(int newWidth) override; + +private: + using ListWidget = PeerListContent; + + // PeerListContentDelegate interface. + void peerListSetTitle(base::lambda title) override; + void peerListSetAdditionalTitle( + base::lambda title) override; + bool peerListIsRowSelected(not_null peer) override; + int peerListSelectedRowsCount() override; + std::vector> peerListCollectSelectedRows() override; + void peerListScrollToTop() override; + void peerListAddSelectedRowInBunch( + not_null peer) override; + void peerListFinishSelectedRowsBunch() override; + void peerListSetDescription( + object_ptr description) override; + + void setupHeader(); + object_ptr setupTitle(); + void setupList(); + + void setupButtons(); + void addChannel(); + void showChannelsWithSearch(bool withSearch); + void updateHeaderControlsGeometry(int newWidth); + + not_null _controller; + not_null _feed; + std::unique_ptr _listController; + object_ptr _header = { nullptr }; + object_ptr _list = { nullptr }; + + Profile::Button *_openChannels = nullptr; + Ui::RpWidget *_titleWrap = nullptr; + Ui::FlatLabel *_title = nullptr; + Ui::IconButton *_addChannel = nullptr; + Ui::IconButton *_search = nullptr; + + rpl::event_stream _scrollToRequests; + +}; + +} // namespace FeedProfile +} // namespace Info diff --git a/Telegram/SourceFiles/info/feed/info_feed_cover.cpp b/Telegram/SourceFiles/info/feed/info_feed_cover.cpp new file mode 100644 index 000000000..132001db4 --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_cover.cpp @@ -0,0 +1,191 @@ +/* +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_cover.h" + +#include "data/data_feed.h" +#include "info/info_controller.h" +#include "lang/lang_keys.h" +#include "ui/widgets/labels.h" +#include "styles/style_info.h" + +namespace Info { +namespace FeedProfile { + +Cover::Cover( + QWidget *parent, + not_null controller) +: FixedHeightWidget( + parent, + st::infoProfilePhotoTop + + st::infoProfilePhoto.size.height() + + st::infoProfilePhotoBottom) +, _controller(controller) +, _feed(_controller->key().feed()) +//, _userpic( +// this, +// controller->parentController(), +// _peer, +// Ui::UserpicButton::Role::OpenPhoto, +// st::infoProfilePhoto) +, _name(this, st::infoProfileNameLabel) +, _status( + this, + st::infoProfileMegagroupStatusLabel) { + _name->setSelectable(true); + _name->setContextCopyText(lang(lng_profile_copy_fullname)); + + initViewers(); + setupChildGeometry(); +} + +void Cover::setupChildGeometry() { + using namespace rpl::mappers; + // + // Visual Studio 2017 15.5.1 internal compiler error here. + // See https://developercommunity.visualstudio.com/content/problem/165155/ice-regression-in-1551-after-successfull-build-in.html + // + //rpl::combine( + // toggleShownValue(), + // widthValue(), + // _2 + //) | rpl::map([](bool shown, int width) { + + //rpl::combine( + // toggleShownValue(), + // widthValue() + //) | rpl::map([](bool shown, int width) { + // return width; + //}) | rpl::start_with_next([this](int newWidth) { + // _userpic->moveToLeft( + // st::infoProfilePhotoLeft, + // st::infoProfilePhotoTop, + // newWidth); + // refreshNameGeometry(newWidth); + // refreshStatusGeometry(newWidth); + //}, lifetime()); +} + +void Cover::initViewers() { + //using Flag = Notify::PeerUpdate::Flag; + //Notify::PeerUpdateValue( + // _peer, + // Flag::NameChanged + //) | rpl::start_with_next( + // [this] { refreshNameText(); }, + // lifetime()); + //Notify::PeerUpdateValue( + // _peer, + // Flag::UserOnlineChanged | Flag::MembersChanged + //) | rpl::start_with_next( + // [this] { refreshStatusText(); }, + // lifetime()); + //if (!_peer->isUser()) { + // Notify::PeerUpdateValue( + // _peer, + // Flag::ChannelRightsChanged | Flag::ChatCanEdit + // ) | rpl::start_with_next( + // [this] { refreshUploadPhotoOverlay(); }, + // lifetime()); + //} + //VerifiedValue( + // _peer + //) | rpl::start_with_next( + // [this](bool verified) { setVerified(verified); }, + // lifetime()); +} + +void Cover::refreshUploadPhotoOverlay() { + //_userpic->switchChangePhotoOverlay([&] { + // if (auto chat = _peer->asChat()) { + // return chat->canEdit(); + // } else if (auto channel = _peer->asChannel()) { + // return channel->canEditInformation(); + // } + // return false; + //}()); +} + +void Cover::refreshNameText() { + _name->setText(_feed->chatsListName()); + refreshNameGeometry(width()); +} + +void Cover::refreshStatusText() { + //auto hasMembersLink = [&] { + // if (auto megagroup = _peer->asMegagroup()) { + // return megagroup->canViewMembers(); + // } + // return false; + //}(); + //auto statusText = [&] { + // auto currentTime = unixtime(); + // if (auto user = _peer->asUser()) { + // const auto result = Data::OnlineTextFull(user, currentTime); + // const auto showOnline = Data::OnlineTextActive(user, currentTime); + // const auto updateIn = Data::OnlineChangeTimeout(user, currentTime); + // if (showOnline) { + // _refreshStatusTimer.callOnce(updateIn); + // } + // return showOnline + // ? textcmdLink(1, result) + // : result; + // } else if (auto chat = _peer->asChat()) { + // if (!chat->amIn()) { + // return lang(lng_chat_status_unaccessible); + // } + // auto fullCount = std::max( + // chat->count, + // int(chat->participants.size())); + // return ChatStatusText(fullCount, _onlineCount, true); + // } else if (auto channel = _peer->asChannel()) { + // auto fullCount = qMax(channel->membersCount(), 1); + // auto result = ChatStatusText( + // fullCount, + // _onlineCount, + // channel->isMegagroup()); + // return hasMembersLink ? textcmdLink(1, result) : result; + // } + // return lang(lng_chat_status_unaccessible); + //}(); + //_status->setRichText(statusText); + //if (hasMembersLink) { + // _status->setLink(1, std::make_shared([=] { + // _controller->showSection(Info::Memento( + // _controller->peerId(), + // Section::Type::Members)); + // })); + //} + refreshStatusGeometry(width()); +} + +Cover::~Cover() { +} + +void Cover::refreshNameGeometry(int newWidth) { + auto nameLeft = st::infoProfileNameLeft; + auto nameTop = st::infoProfileNameTop; + auto nameWidth = newWidth + - nameLeft + - st::infoProfileNameRight; + _name->resizeToNaturalWidth(nameWidth); + _name->moveToLeft(nameLeft, nameTop, newWidth); +} + +void Cover::refreshStatusGeometry(int newWidth) { + auto statusWidth = newWidth + - st::infoProfileStatusLeft + - st::infoProfileStatusRight; + _status->resizeToWidth(statusWidth); + _status->moveToLeft( + st::infoProfileStatusLeft, + st::infoProfileStatusTop, + newWidth); +} + +} // namespace FeedProfile +} // namespace Info diff --git a/Telegram/SourceFiles/info/feed/info_feed_cover.h b/Telegram/SourceFiles/info/feed/info_feed_cover.h new file mode 100644 index 000000000..cb4609f40 --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_cover.h @@ -0,0 +1,64 @@ +/* +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 +*/ +#pragma once + +#include "ui/wrap/padding_wrap.h" +#include "ui/widgets/checkbox.h" +#include "base/timer.h" + +namespace style { +struct InfoToggle; +} // namespace style + +namespace Data { +class Feed; +} // namespace Data + +namespace Ui { +class UserpicButton; +class FlatLabel; +template +class SlideWrap; +} // namespace Ui + +namespace Info { +class Controller; +} // namespace Info + +namespace Info { +namespace FeedProfile { + +class Cover : public Ui::FixedHeightWidget { +public: + Cover( + QWidget *parent, + not_null controller); + + ~Cover(); + +private: + void setupChildGeometry(); + void initViewers(); + void refreshNameText(); + void refreshStatusText(); + void refreshNameGeometry(int newWidth); + void refreshStatusGeometry(int newWidth); + void refreshUploadPhotoOverlay(); + + not_null _controller; + not_null _feed; + + //object_ptr _userpic; + object_ptr _name = { nullptr }; + object_ptr _status = { nullptr }; + //object_ptr _dropArea = { nullptr }; + +}; + +} // namespace FeedProfile +} // namespace Info diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp new file mode 100644 index 000000000..6f3b813fd --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp @@ -0,0 +1,115 @@ +/* +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_profile_inner_widget.h" + +#include "info/info_controller.h" +#include "info/feed/info_feed_profile_widget.h" +#include "info/feed/info_feed_cover.h" +#include "info/feed/info_feed_channels.h" +#include "ui/widgets/scroll_area.h" +#include "ui/wrap/vertical_layout.h" + +namespace Info { +namespace FeedProfile { + +InnerWidget::InnerWidget( + QWidget *parent, + not_null controller) +: RpWidget(parent) +, _controller(controller) +, _feed(_controller->key().feed()) +, _content(setupContent(this)) { + _content->heightValue( + ) | rpl::start_with_next([this](int height) { + if (!_inResize) { + resizeToWidth(width()); + updateDesiredHeight(); + } + }, lifetime()); +} + +object_ptr InnerWidget::setupContent( + not_null parent) { + auto result = object_ptr(parent); + _cover = result->add(object_ptr( + result, + _controller)); + //auto details = SetupDetails(_controller, parent, _peer); + //result->add(std::move(details)); + //if (auto members = SetupChannelMembers(_controller, result.data(), _peer)) { + // result->add(std::move(members)); + //} + //result->add(object_ptr(result)); + //if (auto actions = SetupActions(_controller, result.data(), _peer)) { + // result->add(std::move(actions)); + //} + + //_channels = result->add(object_ptr( + // result, + // _controller) + //); + //_channels->scrollToRequests( + //) | rpl::start_with_next([this](Ui::ScrollToRequest request) { + // auto min = (request.ymin < 0) + // ? request.ymin + // : mapFromGlobal(_channels->mapToGlobal({ 0, request.ymin })).y(); + // auto max = (request.ymin < 0) + // ? mapFromGlobal(_channels->mapToGlobal({ 0, 0 })).y() + // : (request.ymax < 0) + // ? request.ymax + // : mapFromGlobal(_channels->mapToGlobal({ 0, request.ymax })).y(); + // _scrollToRequests.fire({ min, max }); + //}, _channels->lifetime()); + + return std::move(result); +} + +int InnerWidget::countDesiredHeight() const { + return _content->height() + (_channels + ? (_channels->desiredHeight() - _channels->height()) + : 0); +} + +void InnerWidget::visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) { + setChildVisibleTopBottom(_content, visibleTop, visibleBottom); +} + +void InnerWidget::saveState(not_null memento) { + if (_channels) { + memento->setChannelsState(_channels->saveState()); + } +} + +void InnerWidget::restoreState(not_null memento) { + if (_channels) { + _channels->restoreState(memento->channelsState()); + } +} + +rpl::producer InnerWidget::scrollToRequests() const { + return _scrollToRequests.events(); +} + +rpl::producer InnerWidget::desiredHeightValue() const { + return _desiredHeight.events_starting_with(countDesiredHeight()); +} + +int InnerWidget::resizeGetHeight(int newWidth) { + _inResize = true; + auto guard = gsl::finally([&] { _inResize = false; }); + + _content->resizeToWidth(newWidth); + _content->moveToLeft(0, 0); + updateDesiredHeight(); + return _content->heightNoMargins(); +} + +} // namespace FeedProfile +} // namespace Info diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h new file mode 100644 index 000000000..105606477 --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h @@ -0,0 +1,81 @@ +/* +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 +*/ +#pragma once + +#include +#include "ui/rp_widget.h" + +namespace Window { +class Controller; +} // namespace Window + +namespace Ui { +class VerticalLayout; +template +class SlideWrap; +struct ScrollToRequest; +class MultiSlideTracker; +} // namespace Ui + +namespace Info { + +enum class Wrap; +class Controller; + +namespace FeedProfile { + +class Memento; +class Channels; +class Cover; + +class InnerWidget final : public Ui::RpWidget { +public: + InnerWidget( + QWidget *parent, + not_null controller); + + void saveState(not_null memento); + void restoreState(not_null memento); + + void setIsStackBottom(bool isStackBottom) { + _isStackBottom = isStackBottom; + } + rpl::producer scrollToRequests() const; + rpl::producer desiredHeightValue() const override; + +protected: + int resizeGetHeight(int newWidth) override; + void visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) override; + +private: + object_ptr setupContent(not_null parent); + + int countDesiredHeight() const; + void updateDesiredHeight() { + _desiredHeight.fire(countDesiredHeight()); + } + + rpl::variable _isStackBottom = true; + + const not_null _controller; + const not_null _feed; + + Channels *_channels = nullptr; + Cover *_cover = nullptr; + object_ptr _content; + + bool _inResize = false; + rpl::event_stream _scrollToRequests; + rpl::event_stream _desiredHeight; + +}; + +} // namespace FeedProfile +} // namespace Info diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp new file mode 100644 index 000000000..bf77083ea --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp @@ -0,0 +1,116 @@ +/* +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_profile_widget.h" + +#include "info/feed/info_feed_profile_inner_widget.h" +#include "info/feed/info_feed_channels.h" +#include "ui/widgets/scroll_area.h" +#include "info/info_controller.h" + +namespace Info { +namespace FeedProfile { + +Memento::Memento(not_null controller) +: Memento(controller->feed()) { +} + +Memento::Memento(not_null feed) +: ContentMemento(feed) { +} + +Section Memento::section() const { + return Section(Section::Type::Profile); +} + +object_ptr Memento::createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) { + auto result = object_ptr( + parent, + controller); + result->setInternalState(geometry, this); + return std::move(result); +} + +void Memento::setChannelsState(std::unique_ptr state) { + _channelsState = std::move(state); +} + +std::unique_ptr Memento::channelsState() { + return std::move(_channelsState); +} + +Memento::~Memento() = default; + +Widget::Widget( + QWidget *parent, + not_null controller) +: ContentWidget(parent, controller) { + controller->setSearchEnabledByContent(false); + + _inner = setInnerWidget(object_ptr( + this, + controller)); + _inner->move(0, 0); + _inner->scrollToRequests( + ) | rpl::start_with_next([this](Ui::ScrollToRequest request) { + if (request.ymin < 0) { + scrollTopRestore( + qMin(scrollTopSave(), request.ymax)); + } else { + scrollTo(request); + } + }, lifetime()); +} + +void Widget::setIsStackBottom(bool isStackBottom) { + _inner->setIsStackBottom(isStackBottom); +} + +void Widget::setInnerFocus() { + _inner->setFocus(); +} + +bool Widget::showInternal(not_null memento) { + if (!controller()->validateMementoPeer(memento)) { + return false; + } + if (auto profileMemento = dynamic_cast(memento.get())) { + restoreState(profileMemento); + return true; + } + return false; +} + +void Widget::setInternalState( + const QRect &geometry, + not_null memento) { + setGeometry(geometry); + Ui::SendPendingMoveResizeEvents(this); + restoreState(memento); +} + +std::unique_ptr Widget::doCreateMemento() { + auto result = std::make_unique(controller()); + saveState(result.get()); + return std::move(result); +} + +void Widget::saveState(not_null memento) { + memento->setScrollTop(scrollTopSave()); + _inner->saveState(memento); +} + +void Widget::restoreState(not_null memento) { + _inner->restoreState(memento); + scrollTopRestore(memento->scrollTop()); +} + +} // namespace FeedProfile +} // namespace Info diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_widget.h b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.h new file mode 100644 index 000000000..0578f13fd --- /dev/null +++ b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.h @@ -0,0 +1,69 @@ +/* +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 +*/ +#pragma once + +#include +#include "info/info_content_widget.h" + +namespace Info { +namespace FeedProfile { + +class InnerWidget; +struct ChannelsState; + +class Memento final : public ContentMemento { +public: + Memento(not_null controller); + Memento(not_null feed); + + object_ptr createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) override; + + Section section() const override; + + void setChannelsState(std::unique_ptr state); + std::unique_ptr channelsState(); + + ~Memento(); + +private: + std::unique_ptr _channelsState; + +}; + +class Widget final : public ContentWidget { +public: + Widget( + QWidget *parent, + not_null controller); + + void setIsStackBottom(bool isStackBottom) override; + + bool showInternal( + not_null memento) override; + + void setInternalState( + const QRect &geometry, + not_null memento); + + void setInnerFocus() override; + +private: + void saveState(not_null memento); + void restoreState(not_null memento); + + std::unique_ptr doCreateMemento() override; + + InnerWidget *_inner = nullptr; + +}; + +} // namespace FeedProfile +} // namespace Info diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index edeb539b2..5db553a5e 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/search_field_controller.h" #include "lang/lang_keys.h" #include "info/profile/info_profile_widget.h" -#include "info/profile/info_profile_members.h" #include "info/media/info_media_widget.h" #include "info/common_groups/info_common_groups_widget.h" #include "info/info_layer_widget.h" diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index cbcb09124..304231dca 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -23,6 +23,10 @@ template class PaddingWrap; } // namespace Ui +namespace Data { +class Feed; +} // namespace Data + namespace Info { class ContentMemento; @@ -115,6 +119,9 @@ public: : _peerId(peerId) , _migratedPeerId(migratedPeerId) { } + ContentMemento(not_null feed) + : _feed(feed) { + } virtual object_ptr createWidget( QWidget *parent, @@ -127,6 +134,9 @@ public: PeerId migratedPeerId() const { return _migratedPeerId; } + Data::Feed *feed() const { + return _feed; + } virtual Section section() const = 0; @@ -160,6 +170,7 @@ public: private: const PeerId _peerId = 0; const PeerId _migratedPeerId = 0; + Data::Feed * const _feed = nullptr; int _scrollTop = 0; QString _searchFieldQuery; bool _searchEnabledByContent = false; diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp index f5173100e..29e46e735 100644 --- a/Telegram/SourceFiles/info/info_controller.cpp +++ b/Telegram/SourceFiles/info/info_controller.cpp @@ -22,6 +22,7 @@ namespace { not_null CorrectPeer(PeerId peerId) { Expects(peerId != 0); + auto result = App::peer(peerId); if (auto to = result->migrateTo()) { return to; @@ -31,6 +32,26 @@ not_null CorrectPeer(PeerId peerId) { } // namespace +Key::Key(not_null peer) : _value(peer) { +} + +Key::Key(not_null feed) : _value(feed) { +} + +PeerData *Key::peer() const { + if (const auto peer = base::get_if>(&_value)) { + return *peer; + } + return nullptr; +} + +Data::Feed *Key::feed() const { + if (const auto feed = base::get_if>(&_value)) { + return *feed; + } + return nullptr; +} + rpl::producer AbstractController::mediaSource( SparseIdsMergedSlice::UniversalMsgId aroundId, int limitBefore, @@ -67,7 +88,9 @@ Controller::Controller( not_null memento) : AbstractController(window) , _widget(widget) -, _peer(App::peer(memento->peerId())) +, _key(memento->peerId() + ? Key(App::peer(memento->peerId())) + : Key(memento->feed())) , _migrated(memento->migratedPeerId() ? App::peer(memento->migratedPeerId()) : nullptr) @@ -77,20 +100,20 @@ Controller::Controller( } void Controller::setupMigrationViewer() { - if (!_peer->isChat() && (!_peer->isChannel() || _migrated != nullptr)) { + const auto peer = _key.peer(); + if (!peer || (!peer->isChat() && !peer->isChannel()) || _migrated) { return; } Notify::PeerUpdateValue( - _peer, + peer, Notify::PeerUpdate::Flag::MigrationChanged - ) | rpl::start_with_next([this] { - if (_peer->migrateTo() || (_peer->migrateFrom() != _migrated)) { - auto window = parentController(); - auto peerId = _peer->id; - auto section = _section; + ) | rpl::start_with_next([=] { + if (peer->migrateTo() || (peer->migrateFrom() != _migrated)) { + const auto window = parentController(); + const auto section = _section; InvokeQueued(_widget, [=] { window->showSection( - Memento(peerId, section), + Memento(peer->id, section), Window::SectionShow( Window::SectionShow::Way::Backward, anim::type::instant, @@ -111,7 +134,8 @@ rpl::producer Controller::wrapValue() const { bool Controller::validateMementoPeer( not_null memento) const { return memento->peerId() == peerId() - && memento->migratedPeerId() == migratedPeerId(); + && memento->migratedPeerId() == migratedPeerId() + && memento->feed() == feed(); } void Controller::setSection(not_null memento) { @@ -193,9 +217,11 @@ void Controller::showBackFromStack(const Window::SectionShow ¶ms) { auto Controller::produceSearchQuery( const QString &query) const -> SearchQuery { + Expects(_key.peer() != nullptr); + auto result = SearchQuery(); result.type = _section.mediaType(); - result.peerId = _peer->id; + result.peerId = _key.peer()->id; result.query = query; result.migratedPeerId = _migrated ? _migrated->id : PeerId(0); return result; diff --git a/Telegram/SourceFiles/info/info_controller.h b/Telegram/SourceFiles/info/info_controller.h index 346fe54a8..9ae08a170 100644 --- a/Telegram/SourceFiles/info/info_controller.h +++ b/Telegram/SourceFiles/info/info_controller.h @@ -17,6 +17,19 @@ class SearchFieldController; namespace Info { +class Key { +public: + Key(not_null peer); + Key(not_null feed); + + PeerData *peer() const; + Data::Feed *feed() const; + +private: + base::variant, not_null> _value; + +}; + enum class Wrap; class WrapWidget; class Memento; @@ -60,12 +73,15 @@ public: : _parent(parent) { } - virtual not_null peer() const = 0; + virtual Key key() const = 0; virtual PeerData *migrated() const = 0; virtual Section section() const = 0; PeerId peerId() const { - return peer()->id; + if (const auto peer = key().peer()) { + return peer->id; + } + return PeerId(0); } PeerId migratedPeerId() const { if (auto peer = migrated()) { @@ -73,6 +89,9 @@ public: } return PeerId(0); } + Data::Feed *feed() const { + return key().feed(); + } virtual void setSearchEnabledByContent(bool enabled) { } @@ -103,8 +122,8 @@ public: not_null window, not_null memento); - not_null peer() const override { - return _peer; + Key key() const override { + return _key; } PeerData *migrated() const override { return _migrated; @@ -158,7 +177,7 @@ private: void setupMigrationViewer(); not_null _widget; - not_null _peer; + Key _key; PeerData *_migrated = nullptr; rpl::variable _wrap; Section _section; diff --git a/Telegram/SourceFiles/info/info_memento.cpp b/Telegram/SourceFiles/info/info_memento.cpp index 7a6069fd5..8a8567c68 100644 --- a/Telegram/SourceFiles/info/info_memento.cpp +++ b/Telegram/SourceFiles/info/info_memento.cpp @@ -8,10 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_memento.h" #include "info/profile/info_profile_widget.h" -#include "info/profile/info_profile_members.h" #include "info/media/info_media_widget.h" #include "info/members/info_members_widget.h" #include "info/common_groups/info_common_groups_widget.h" +#include "info/feed/info_feed_profile_widget.h" #include "info/info_section_widget.h" #include "info/info_layer_widget.h" #include "info/info_controller.h" @@ -27,6 +27,10 @@ Memento::Memento(PeerId peerId, Section section) : Memento(DefaultStack(peerId, section)) { } +Memento::Memento(not_null feed, Section section) +: Memento(DefaultStack(feed, section)) { +} + Memento::Memento(std::vector> stack) : _stack(std::move(stack)) { } @@ -39,14 +43,28 @@ std::vector> Memento::DefaultStack( return result; } -Section Memento::DefaultSection(not_null peer) { - return peer->isSelf() - ? Section(Section::MediaType::Photo) - : Section(Section::Type::Profile); +std::vector> Memento::DefaultStack( + not_null feed, + Section section) { + auto result = std::vector>(); + result.push_back(DefaultContent(feed, section)); + return result; } -Memento Memento::Default(not_null peer) { - return Memento(peer->id, DefaultSection(peer)); +Section Memento::DefaultSection(Dialogs::Key key) { + if (const auto peer = key.peer()) { + if (peer->isSelf()) { + return Section(Section::MediaType::Photo); + } + } + return Section(Section::Type::Profile); +} + +Memento Memento::Default(Dialogs::Key key) { + if (const auto peer = key.peer()) { + return Memento(peer->id, DefaultSection(key)); + } + return Memento(key.feed(), DefaultSection(key)); } std::unique_ptr Memento::DefaultContent( @@ -84,6 +102,16 @@ std::unique_ptr Memento::DefaultContent( Unexpected("Wrong section type in Info::Memento::DefaultContent()"); } +std::unique_ptr Memento::DefaultContent( + not_null feed, + Section section) { + switch (section.type()) { + case Section::Type::Profile: + return std::make_unique(feed); + } + Unexpected("Wrong feed section in Info::Memento::DefaultContent()"); +} + object_ptr Memento::createWidget( QWidget *parent, not_null controller, diff --git a/Telegram/SourceFiles/info/info_memento.h b/Telegram/SourceFiles/info/info_memento.h index 962ee609f..103bcf9c8 100644 --- a/Telegram/SourceFiles/info/info_memento.h +++ b/Telegram/SourceFiles/info/info_memento.h @@ -9,12 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "info/info_wrap_widget.h" +#include "dialogs/dialogs_key.h" #include "window/section_memento.h" namespace Storage { enum class SharedMediaType : char; } // namespace Storage +namespace Data { +class Feed; +} // namespace Data + namespace Ui { class ScrollArea; struct ScrollToRequest; @@ -29,6 +34,7 @@ class Memento final : public Window::SectionMemento { public: Memento(PeerId peerId); Memento(PeerId peerId, Section section); + Memento(not_null feed, Section section); Memento(std::vector> stack); object_ptr createWidget( @@ -51,8 +57,8 @@ public: return _stack.back().get(); } - static Section DefaultSection(not_null peer); - static Memento Default(not_null peer); + static Section DefaultSection(Dialogs::Key key); + static Memento Default(Dialogs::Key key); ~Memento(); @@ -60,6 +66,13 @@ private: static std::vector> DefaultStack( PeerId peerId, Section section); + static std::vector> DefaultStack( + not_null feed, + Section section); + static std::unique_ptr DefaultContent( + not_null feed, + Section section); + static std::unique_ptr DefaultContent( PeerId peerId, Section section); diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 576c97420..332770496 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -527,12 +527,16 @@ void TopBar::performDelete() { rpl::producer TitleValue( const Section §ion, - not_null peer, + Key key, bool isStackBottom) { return Lang::Viewer([&] { + const auto peer = key.peer(); + switch (section.type()) { case Section::Type::Profile: - if (auto user = peer->asUser()) { + if (const auto feed = key.feed()) { + return lng_info_feed_title; + } else if (auto user = peer->asUser()) { return user->botInfo ? lng_info_bot_title : lng_info_user_title; diff --git a/Telegram/SourceFiles/info/info_top_bar.h b/Telegram/SourceFiles/info/info_top_bar.h index 3be57c634..edb91452c 100644 --- a/Telegram/SourceFiles/info/info_top_bar.h +++ b/Telegram/SourceFiles/info/info_top_bar.h @@ -26,11 +26,12 @@ class LabelWithNumbers; namespace Info { +class Key; class Section; rpl::producer TitleValue( const Section §ion, - not_null peer, + Key key, bool isStackBottom); class TopBar : public Ui::RpWidget { diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index a7c413a26..0d5f7cfc8 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include "info/profile/info_profile_widget.h" -#include "info/profile/info_profile_members.h" #include "info/profile/info_profile_values.h" #include "info/media/info_media_widget.h" #include "info/info_content_widget.h" @@ -119,7 +118,7 @@ void WrapWidget::injectActiveProfile(Dialogs::Key key) { if (const auto peer = key.peer()) { injectActivePeerProfile(peer); } else if (const auto feed = key.feed()) { - // #TODO feed profile + injectActiveFeedProfile(feed); } } @@ -147,19 +146,40 @@ void WrapWidget::injectActivePeerProfile(not_null peer) { if (firstSectionType != expectedType || firstSectionMediaType != expectedMediaType || firstPeerId != peer->id) { - auto injected = StackItem(); auto section = peer->isSelf() ? Section(Section::MediaType::Photo) : Section(Section::Type::Profile); - injected.section = std::move( - Memento(peer->id, section).takeStack().front()); - _historyStack.insert( - _historyStack.begin(), - std::move(injected)); - if (_content) { - setupTop(); - finishShowContent(); - } + injectActiveProfileMemento(std::move( + Memento(peer->id, section).takeStack().front())); + } +} + +void WrapWidget::injectActiveFeedProfile(not_null feed) { + const auto firstFeed = hasStackHistory() + ? _historyStack.front().section->feed() + : _controller->feed(); + const auto firstSectionType = hasStackHistory() + ? _historyStack.front().section->section().type() + : _controller->section().type(); + const auto expectedType = Section::Type::Profile; + if (firstSectionType != expectedType + || firstFeed != feed) { + auto section = Section(Section::Type::Profile); + injectActiveProfileMemento(std::move( + Memento(feed, section).takeStack().front())); + } +} + +void WrapWidget::injectActiveProfileMemento( + std::unique_ptr memento) { + auto injected = StackItem(); + injected.section = std::move(memento); + _historyStack.insert( + _historyStack.begin(), + std::move(injected)); + if (_content) { + setupTop(); + finishShowContent(); } } @@ -173,12 +193,17 @@ std::unique_ptr WrapWidget::createController( return result; } -not_null WrapWidget::peer() const { - return _controller->peer(); +Key WrapWidget::key() const { + return _controller->key(); } Dialogs::RowDescriptor WrapWidget::activeChat() const { - return Dialogs::RowDescriptor(App::history(peer()), MsgId(0)); + if (const auto peer = key().peer()) { + return Dialogs::RowDescriptor(App::history(peer), MsgId(0)); + } else if (const auto feed = key().feed()) { + return Dialogs::RowDescriptor(feed, MsgId(0)); + } + Unexpected("Owner in WrapWidget::activeChat()."); } // This was done for tabs support. @@ -320,7 +345,7 @@ void WrapWidget::createTopBar() { _topBar->setTitle(TitleValue( _controller->section(), - _controller->peer(), + _controller->key(), !hasStackHistory())); if (wrapValue == Wrap::Narrow || hasStackHistory()) { _topBar->enableBackButton(); @@ -383,7 +408,8 @@ void WrapWidget::addProfileMenuButton() { void WrapWidget::addProfileCallsButton() { Expects(_topBar != nullptr); - const auto user = _controller->peer()->asUser(); + const auto peer = key().peer(); + const auto user = peer ? peer->asUser() : nullptr; if (!user || user->isSelf() || !Global::PhoneCallsEnabled()) { return; } @@ -415,7 +441,10 @@ void WrapWidget::addProfileCallsButton() { void WrapWidget::addProfileNotificationsButton() { Expects(_topBar != nullptr); - const auto peer = _controller->peer(); + const auto peer = key().peer(); + if (!peer) { + return; + } auto notifications = _topBar->addButton( base::make_unique_q( _topBar, @@ -468,9 +497,14 @@ void WrapWidget::showProfileMenu() { }); _topBarMenuToggle->installEventFilter(_topBarMenu.get()); + // #TODO feeds menu + const auto peer = key().peer(); + if (!peer) { + return; + } Window::FillPeerMenu( _controller->parentController(), - _controller->peer(), + peer, [this](const QString &text, base::lambda callback) { return _topBarMenu->addAction(text, std::move(callback)); }, diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h index 0963a9c6f..72c41be6d 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.h +++ b/Telegram/SourceFiles/info/info_wrap_widget.h @@ -15,6 +15,10 @@ namespace Storage { enum class SharedMediaType : char; } // namespace Storage +namespace Data { +class Feed; +} // namespace Data + namespace Ui { class SettingsSlider; class FadeShadow; @@ -36,6 +40,7 @@ namespace Media { class Widget; } // namespace Media +class Key; class Controller; class Section; class Memento; @@ -77,7 +82,7 @@ public: Wrap wrap, not_null memento); - not_null peer() const; + Key key() const; Dialogs::RowDescriptor activeChat() const override; Wrap wrap() const { return _wrap.current(); @@ -140,6 +145,9 @@ private: void startInjectingActivePeerProfiles(); void injectActiveProfile(Dialogs::Key key); void injectActivePeerProfile(not_null peer); + void injectActiveFeedProfile(not_null feed); + void injectActiveProfileMemento( + std::unique_ptr memento); void restoreHistoryStack( std::vector> stack); bool hasStackHistory() const { diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp index 75bf940cd..a9f55539b 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp @@ -42,7 +42,7 @@ InnerWidget::InnerWidget( // Was done for top level tabs support. // Now used for shared media in Saved Messages. void InnerWidget::setupOtherTypes() { - if (_controller->peer()->isSelf() && _isStackBottom) { + if (_controller->key().peer()->isSelf() && _isStackBottom) { createOtherTypes(); } else { _otherTypes.destroy(); @@ -95,7 +95,7 @@ void InnerWidget::createTypeButtons() { auto result = AddButton( content, _controller, - _controller->peer(), + _controller->key().peer(), _controller->migrated(), buttonType, tracker); @@ -123,7 +123,7 @@ void InnerWidget::createTypeButtons() { addMediaButton(Type::File, st::infoIconMediaFile); addMediaButton(Type::MusicFile, st::infoIconMediaAudio); addMediaButton(Type::Link, st::infoIconMediaLink); - if (auto user = _controller->peer()->asUser()) { + if (auto user = _controller->key().peer()->asUser()) { // addCommonGroupsButton(user, st::infoIconMediaGroup); } addMediaButton(Type::VoiceFile, st::infoIconMediaVoice); diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index fb7b71582..ea647b1a0 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -541,7 +541,7 @@ ListWidget::ListWidget( not_null controller) : RpWidget(parent) , _controller(controller) -, _peer(_controller->peer()) +, _peer(_controller->key().peer()) , _migrated(_controller->migrated()) , _type(_controller->section().mediaType()) , _slice(sliceKey(_universalAroundId)) { diff --git a/Telegram/SourceFiles/info/members/info_members_widget.cpp b/Telegram/SourceFiles/info/members/info_members_widget.cpp index ab0d7b9e2..d79a7d07b 100644 --- a/Telegram/SourceFiles/info/members/info_members_widget.cpp +++ b/Telegram/SourceFiles/info/members/info_members_widget.cpp @@ -21,6 +21,10 @@ Memento::Memento(not_null controller) controller->migratedPeerId()) { } +Memento::Memento(PeerId peerId, PeerId migratedPeerId) +: ContentMemento(peerId, migratedPeerId) { +} + Section Memento::section() const { return Section(Section::Type::Members); } @@ -52,8 +56,7 @@ Widget::Widget( : ContentWidget(parent, controller) { _inner = setInnerWidget(object_ptr( this, - controller, - controller->peer())); + controller)); } bool Widget::showInternal(not_null memento) { diff --git a/Telegram/SourceFiles/info/members/info_members_widget.h b/Telegram/SourceFiles/info/members/info_members_widget.h index 66d89a804..f7159336a 100644 --- a/Telegram/SourceFiles/info/members/info_members_widget.h +++ b/Telegram/SourceFiles/info/members/info_members_widget.h @@ -25,9 +25,7 @@ using SavedState = Profile::MembersState; class Memento final : public ContentMemento { public: Memento(not_null controller); - Memento(PeerId peerId, PeerId migratedPeerId) - : ContentMemento(peerId, migratedPeerId) { - } + Memento(PeerId peerId, PeerId migratedPeerId); object_ptr createWidget( QWidget *parent, diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index 68a8e7d0c..3fb78a8fa 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -212,15 +212,14 @@ int SectionWithToggle::toggleSkip() const { Cover::Cover( QWidget *parent, - not_null controller, - not_null peer) + not_null controller) : SectionWithToggle( parent, st::infoProfilePhotoTop + st::infoProfilePhoto.size.height() + st::infoProfilePhotoBottom) , _controller(controller) -, _peer(peer) +, _peer(_controller->key().peer()) , _userpic( this, controller->parentController(), diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.h b/Telegram/SourceFiles/info/profile/info_profile_cover.h index 47c0b8e96..07ba89205 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.h +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.h @@ -52,8 +52,7 @@ class Cover : public SectionWithToggle { public: Cover( QWidget *parent, - not_null controller, - not_null peer); + not_null controller); Cover *setOnlineCount(rpl::producer &&count); diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index 77ef14136..011045463 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -50,7 +50,7 @@ InnerWidget::InnerWidget( not_null controller) : RpWidget(parent) , _controller(controller) -, _peer(_controller->peer()) +, _peer(_controller->key().peer()) , _migrated(_controller->migrated()) , _content(setupContent(this)) { _content->heightValue( @@ -77,8 +77,7 @@ object_ptr InnerWidget::setupContent( auto result = object_ptr(parent); _cover = result->add(object_ptr( result, - _controller, - _peer)); + _controller)); _cover->setOnlineCount(rpl::single(0)); auto details = SetupDetails(_controller, parent, _peer); if (canHideDetailsEver()) { @@ -106,9 +105,7 @@ object_ptr InnerWidget::setupContent( if (_peer->isChat() || _peer->isMegagroup()) { _members = result->add(object_ptr( result, - _controller, - _peer) - ); + _controller)); _members->scrollToRequests( ) | rpl::start_with_next([this](Ui::ScrollToRequest request) { auto min = (request.ymin < 0) diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.cpp b/Telegram/SourceFiles/info/profile/info_profile_members.cpp index b4676e259..aa41986e4 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_members.cpp @@ -41,11 +41,10 @@ constexpr auto kEnableSearchMembersAfterCount = 20; Members::Members( QWidget *parent, - not_null controller, - not_null peer) + not_null controller) : RpWidget(parent) , _controller(controller) -, _peer(peer) +, _peer(_controller->key().peer()) , _listController(CreateMembersController(controller, _peer)) { setupHeader(); setupList(); diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.h b/Telegram/SourceFiles/info/profile/info_profile_members.h index 4a7fd9f63..8e3aed0ba 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_members.h +++ b/Telegram/SourceFiles/info/profile/info_profile_members.h @@ -20,7 +20,6 @@ class AbstractButton; } // namespace Ui namespace Profile { -class GroupMembersWidget; class ParticipantsBoxController; } // namespace Profile @@ -44,8 +43,7 @@ class Members public: Members( QWidget *parent, - not_null controller, - not_null peer); + not_null controller); rpl::producer scrollToRequests() const; diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp index 1c803c030..c6069e2e0 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp @@ -21,6 +21,10 @@ Memento::Memento(not_null controller) controller->migratedPeerId()) { } +Memento::Memento(PeerId peerId, PeerId migratedPeerId) +: ContentMemento(peerId, migratedPeerId) { +} + Section Memento::section() const { return Section(Section::Type::Profile); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.h b/Telegram/SourceFiles/info/profile/info_profile_widget.h index 935603a9b..fa1b098df 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_widget.h +++ b/Telegram/SourceFiles/info/profile/info_profile_widget.h @@ -19,9 +19,7 @@ struct MembersState; class Memento final : public ContentMemento { public: Memento(not_null controller); - Memento(PeerId peerId, PeerId migratedPeerId) - : ContentMemento(peerId, migratedPeerId) { - } + Memento(PeerId peerId, PeerId migratedPeerId); object_ptr createWidget( QWidget *parent, @@ -43,7 +41,6 @@ public: private: bool _infoExpanded = true; - base::optional _membersSearch; std::unique_ptr _membersState; }; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 61b1616ef..b78aa9a25 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3009,7 +3009,11 @@ void MainWidget::updateControlsGeometry() { if (Auth().settings().tabbedSelectorSectionEnabled()) { _history->pushTabbedSelectorToThirdSection(params); } else if (Auth().settings().thirdSectionInfoEnabled()) { - _history->pushInfoToThirdSection(params); + if (const auto key = _controller->activeChatCurrent()) { + _controller->showSection( + Info::Memento::Default(key), + params.withThirdColumn()); + } } } } else { @@ -3206,12 +3210,13 @@ auto MainWidget::thirdSectionForCurrentMainSection( } else if (const auto peer = key.peer()) { return std::make_unique( peer->id, - Info::Memento::DefaultSection(peer)); - } else { + Info::Memento::DefaultSection(key)); + } else if (const auto feed = key.feed()) { return std::make_unique( - App::self()->id, - Info::Memento::DefaultSection(App::self())); + feed, + Info::Memento::DefaultSection(key)); } + Unexpected("Key in MainWidget::thirdSectionForCurrentMainSection()."); } void MainWidget::updateThirdColumnToCurrentChat( diff --git a/Telegram/SourceFiles/media/player/media_player_panel.cpp b/Telegram/SourceFiles/media/player/media_player_panel.cpp index 3c1df1f10..2c4500750 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.cpp +++ b/Telegram/SourceFiles/media/player/media_player_panel.cpp @@ -355,8 +355,8 @@ void Panel::setCloseCallback(ButtonCallback &&callback) { } } -not_null Panel::peer() const { - return _listPeer; +Info::Key Panel::key() const { + return Info::Key(_listPeer); } PeerData *Panel::migrated() const { diff --git a/Telegram/SourceFiles/media/player/media_player_panel.h b/Telegram/SourceFiles/media/player/media_player_panel.h index 9a61e5cf6..9ee278342 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.h +++ b/Telegram/SourceFiles/media/player/media_player_panel.h @@ -57,7 +57,7 @@ protected: private: // Info::AbstractController implementation. - not_null peer() const override; + Info::Key key() const override; PeerData *migrated() const override; Info::Section section() const override; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index f1f1ed941..64ca0f239 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -284,6 +284,14 @@ <(src_loc)/info/common_groups/info_common_groups_inner_widget.h <(src_loc)/info/common_groups/info_common_groups_widget.cpp <(src_loc)/info/common_groups/info_common_groups_widget.h +<(src_loc)/info/feed/info_feed_channels.cpp +<(src_loc)/info/feed/info_feed_channels.h +<(src_loc)/info/feed/info_feed_cover.cpp +<(src_loc)/info/feed/info_feed_cover.h +<(src_loc)/info/feed/info_feed_profile_inner_widget.cpp +<(src_loc)/info/feed/info_feed_profile_inner_widget.h +<(src_loc)/info/feed/info_feed_profile_widget.cpp +<(src_loc)/info/feed/info_feed_profile_widget.h <(src_loc)/info/media/info_media_buttons.h <(src_loc)/info/media/info_media_empty_widget.cpp <(src_loc)/info/media/info_media_empty_widget.h