/* 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/info_controller.h" #include "ui/search_field_controller.h" #include "data/data_shared_media.h" #include "info/info_content_widget.h" #include "info/info_memento.h" #include "info/media/info_media_widget.h" #include "core/application.h" #include "data/data_changes.h" #include "data/data_peer.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_forum_topic.h" #include "data/data_forum.h" #include "data/data_session.h" #include "data/data_media_types.h" #include "data/data_download_manager.h" #include "history/history_item.h" #include "main/main_session.h" #include "window/window_session_controller.h" namespace Info { Key::Key(not_null peer) : _value(peer) { } Key::Key(not_null topic) : _value(topic) { } Key::Key(Settings::Tag settings) : _value(settings) { } Key::Key(Downloads::Tag downloads) : _value(downloads) { } Key::Key(Stories::Tag stories) : _value(stories) { } Key::Key(Statistics::Tag statistics) : _value(statistics) { } Key::Key(not_null poll, FullMsgId contextId) : _value(PollKey{ poll, contextId }) { } PeerData *Key::peer() const { if (const auto peer = std::get_if>(&_value)) { return *peer; } else if (const auto topic = this->topic()) { return topic->channel(); } return nullptr; } Data::ForumTopic *Key::topic() const { if (const auto topic = std::get_if>( &_value)) { return *topic; } return nullptr; } UserData *Key::settingsSelf() const { if (const auto tag = std::get_if(&_value)) { return tag->self; } return nullptr; } bool Key::isDownloads() const { return v::is(_value); } PeerData *Key::storiesPeer() const { if (const auto tag = std::get_if(&_value)) { return tag->peer; } return nullptr; } Stories::Tab Key::storiesTab() const { if (const auto tag = std::get_if(&_value)) { return tag->tab; } return Stories::Tab(); } PeerData *Key::statisticsPeer() const { if (const auto tag = std::get_if(&_value)) { return tag->peer; } return nullptr; } FullMsgId Key::statisticsContextId() const { if (const auto tag = std::get_if(&_value)) { return tag->contextId; } return {}; } PollData *Key::poll() const { if (const auto data = std::get_if(&_value)) { return data->poll; } return nullptr; } FullMsgId Key::pollContextId() const { if (const auto data = std::get_if(&_value)) { return data->contextId; } return FullMsgId(); } rpl::producer AbstractController::mediaSource( SparseIdsMergedSlice::UniversalMsgId aroundId, int limitBefore, int limitAfter) const { Expects(peer() != nullptr); const auto isScheduled = [&] { const auto peerId = peer()->id; if (const auto item = session().data().message(peerId, aroundId)) { return item->isScheduled(); } return false; }(); const auto mediaViewer = isScheduled ? SharedScheduledMediaViewer : SharedMediaMergedViewer; const auto topicId = isScheduled ? SparseIdsMergedSlice::kScheduledTopicId : topic() ? topic()->rootId() : MsgId(0); return mediaViewer( &session(), SharedMediaMergedKey( SparseIdsMergedSlice::Key( peer()->id, topicId, migratedPeerId(), aroundId), section().mediaType()), limitBefore, limitAfter); } rpl::producer AbstractController::mediaSourceQueryValue() const { return rpl::single(QString()); } rpl::producer AbstractController::searchQueryValue() const { return rpl::single(QString()); } AbstractController::AbstractController( not_null parent) : SessionNavigation(&parent->session()) , _parent(parent) { } PeerData *AbstractController::peer() const { return key().peer(); } PeerId AbstractController::migratedPeerId() const { if (const auto peer = migrated()) { return peer->id; } return PeerId(0); } PollData *AbstractController::poll() const { if (const auto item = session().data().message(pollContextId())) { if (const auto media = item->media()) { return media->poll(); } } return nullptr; } void AbstractController::showSection( std::shared_ptr memento, const Window::SectionShow ¶ms) { return parentController()->showSection(std::move(memento), params); } void AbstractController::showBackFromStack( const Window::SectionShow ¶ms) { return parentController()->showBackFromStack(params); } void AbstractController::showPeerHistory( PeerId peerId, const Window::SectionShow ¶ms, MsgId msgId) { return parentController()->showPeerHistory(peerId, params, msgId); } Controller::Controller( not_null widget, not_null window, not_null memento) : AbstractController(window) , _widget(widget) , _key(memento->key()) , _migrated(memento->migratedPeerId() ? window->session().data().peer(memento->migratedPeerId()).get() : nullptr) , _section(memento->section()) { updateSearchControllers(memento); setupMigrationViewer(); setupTopicViewer(); } void Controller::setupMigrationViewer() { const auto peer = _key.peer(); if (_key.topic() || !peer || (!peer->isChat() && !peer->isChannel()) || _migrated) { return; } peer->session().changes().peerFlagsValue( peer, Data::PeerUpdate::Flag::Migration ) | rpl::filter([=] { return peer->migrateTo() || (peer->migrateFrom() != _migrated); }) | rpl::start_with_next([=] { replaceWith(std::make_shared(peer, _section)); }, lifetime()); } void Controller::replaceWith(std::shared_ptr memento) { const auto window = parentController(); auto params = Window::SectionShow( Window::SectionShow::Way::Backward, anim::type::instant, anim::activation::background); if (wrap() == Wrap::Side) { params.thirdColumn = true; } InvokeQueued(_widget, [=, memento = std::move(memento)]() mutable { window->showSection(std::move(memento), params); }); } void Controller::setupTopicViewer() { session().data().itemIdChanged( ) | rpl::start_with_next([=](const Data::Session::IdChange &change) { if (const auto topic = _key.topic()) { if (topic->rootId() == change.oldId || (topic->peer()->id == change.newId.peer && topic->rootId() == change.newId.msg)) { const auto now = topic->forum()->topicFor(change.newId.msg); _key = Key(now); replaceWith(std::make_shared(now, _section)); } } }, _lifetime); } Wrap Controller::wrap() const { return _widget->wrap(); } rpl::producer Controller::wrapValue() const { return _widget->wrapValue(); } bool Controller::validateMementoPeer( not_null memento) const { return memento->peer() == peer() && memento->migratedPeerId() == migratedPeerId() && memento->settingsSelf() == settingsSelf() && memento->storiesPeer() == storiesPeer() && memento->statisticsPeer() == statisticsPeer(); } void Controller::setSection(not_null memento) { _section = memento->section(); updateSearchControllers(memento); } void Controller::updateSearchControllers( not_null memento) { using Type = Section::Type; const auto type = _section.type(); const auto isMedia = (type == Type::Media); const auto mediaType = isMedia ? _section.mediaType() : Section::MediaType::kCount; const auto hasMediaSearch = isMedia && SharedMediaAllowSearch(mediaType); const auto hasCommonGroupsSearch = (type == Type::CommonGroups); const auto hasDownloadsSearch = (type == Type::Downloads); const auto hasMembersSearch = (type == Type::Members) || (type == Type::Profile); const auto searchQuery = memento->searchFieldQuery(); if (isMedia) { _searchController = std::make_unique(&session()); auto mediaMemento = dynamic_cast(memento.get()); Assert(mediaMemento != nullptr); _searchController->restoreState( mediaMemento->searchState()); } else { _searchController = nullptr; } if (hasMediaSearch || hasCommonGroupsSearch || hasDownloadsSearch || hasMembersSearch) { _searchFieldController = std::make_unique( searchQuery); if (_searchController) { _searchFieldController->queryValue( ) | rpl::start_with_next([=](QString &&query) { _searchController->setQuery( produceSearchQuery(std::move(query))); }, _searchFieldController->lifetime()); } _seachEnabledByContent = memento->searchEnabledByContent(); _searchStartsFocused = memento->searchStartsFocused(); } else { _searchFieldController = nullptr; } } void Controller::saveSearchState(not_null memento) { if (_searchFieldController) { memento->setSearchFieldQuery( _searchFieldController->query()); memento->setSearchEnabledByContent( _seachEnabledByContent.current()); } if (_searchController) { auto mediaMemento = dynamic_cast( memento.get()); Assert(mediaMemento != nullptr); mediaMemento->setSearchState(_searchController->saveState()); } } void Controller::showSection( std::shared_ptr memento, const Window::SectionShow ¶ms) { if (!_widget->showInternal(memento.get(), params)) { AbstractController::showSection(std::move(memento), params); } } void Controller::showBackFromStack(const Window::SectionShow ¶ms) { if (!_widget->showBackFromStackInternal(params)) { AbstractController::showBackFromStack(params); } } void Controller::removeFromStack(const std::vector
§ions) const { _widget->removeFromStack(sections); } auto Controller::produceSearchQuery( const QString &query) const -> SearchQuery { Expects(_key.peer() != nullptr); auto result = SearchQuery(); result.type = _section.mediaType(); result.peerId = _key.peer()->id; result.topicRootId = _key.topic() ? _key.topic()->rootId() : 0; result.query = query; result.migratedPeerId = _migrated ? _migrated->id : PeerId(0); return result; } rpl::producer Controller::searchEnabledByContent() const { return _seachEnabledByContent.value(); } rpl::producer Controller::mediaSourceQueryValue() const { return _searchController->currentQueryValue(); } rpl::producer Controller::searchQueryValue() const { return searchFieldController()->queryValue(); } rpl::producer Controller::mediaSource( SparseIdsMergedSlice::UniversalMsgId aroundId, int limitBefore, int limitAfter) const { auto query = _searchController->currentQuery(); if (!query.query.isEmpty()) { return _searchController->idsSlice( aroundId, limitBefore, limitAfter); } return SharedMediaMergedViewer( &session(), SharedMediaMergedKey( SparseIdsMergedSlice::Key( query.peerId, query.topicRootId, query.migratedPeerId, aroundId), query.type), limitBefore, limitAfter); } std::any &Controller::stepDataReference() { return _stepData; } void Controller::takeStepData(not_null another) { _stepData = base::take(another->_stepData); } Controller::~Controller() = default; } // namespace Info