/* 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/saved/info_saved_music_widget.h" #include "data/data_document.h" #include "data/data_peer.h" #include "data/data_saved_music.h" #include "data/data_session.h" #include "info/media/info_media_list_widget.h" #include "info/info_controller.h" #include "info/info_memento.h" #include "main/main_session.h" #include "ui/text/text_utilities.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/widgets/scroll_area.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "lang/lang_keys.h" #include "ui/ui_utility.h" #include "styles/style_credits.h" // giftListAbout #include "styles/style_info.h" #include "styles/style_layers.h" namespace Info::Saved { class MusicInner final : public Ui::RpWidget { public: MusicInner(QWidget *parent, not_null controller); ~MusicInner(); bool showInternal(not_null memento); void setIsStackBottom(bool isStackBottom) { _isStackBottom = isStackBottom; } void saveState(not_null memento); void restoreState(not_null memento); void setScrollHeightValue(rpl::producer value); rpl::producer scrollToRequests() const; rpl::producer selectedListValue() const; void selectionAction(SelectionAction action); protected: int resizeGetHeight(int newWidth) override; void visibleTopBottomUpdated( int visibleTop, int visibleBottom) override; private: int recountHeight(); void refreshHeight(); void refreshEmpty(); void setupList(); void setupEmpty(); const not_null _controller; const not_null _peer; base::unique_qptr _menu; object_ptr _list = { nullptr }; object_ptr _empty = { nullptr }; int _lastNonLoadingHeight = 0; bool _emptyLoading = false; bool _inResize = false; bool _isStackBottom = false; rpl::event_stream _scrollToRequests; rpl::event_stream> _selectedLists; rpl::event_stream> _listTops; rpl::variable _topHeight; rpl::variable _albumEmpty; }; MusicInner::MusicInner(QWidget *parent, not_null controller) : RpWidget(parent) , _controller(controller) , _peer(controller->key().musicPeer()) { setupList(); setupEmpty(); } MusicInner::~MusicInner() = default; void MusicInner::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { setChildVisibleTopBottom(_list, visibleTop, visibleBottom); } bool MusicInner::showInternal(not_null memento) { if (memento->section().type() == Section::Type::SavedMusic) { restoreState(memento); return true; } return false; } void MusicInner::setupList() { Expects(!_list); _list = object_ptr(this, _controller); if (_peer->isSelf()) { _list->setReorderDescriptor({ .save = [=]( int oldPosition, int newPosition, Fn done, Fn fail) { _controller->session().data().savedMusic().reorder( oldPosition, newPosition); done(); } }); } const auto raw = _list.data(); using namespace rpl::mappers; raw->scrollToRequests( ) | rpl::map([=](int to) { return Ui::ScrollToRequest { raw->y() + to, -1 }; }) | rpl::start_to_stream(_scrollToRequests, raw->lifetime()); _selectedLists.fire(raw->selectedListValue()); _listTops.fire(raw->topValue()); raw->show(); } void MusicInner::setupEmpty() { _list->resizeToWidth(width()); const auto savedMusic = &_controller->session().data().savedMusic(); rpl::combine( rpl::single( rpl::empty ) | rpl::then( savedMusic->changed() | rpl::filter( rpl::mappers::_1 == _peer->id ) | rpl::to_empty ), _list->heightValue() ) | rpl::on_next([=](auto, int listHeight) { const auto padding = st::infoMediaMargin; if (const auto raw = _empty.release()) { raw->hide(); raw->deleteLater(); } _emptyLoading = false; if (listHeight <= padding.bottom() + padding.top()) { refreshEmpty(); } else { _albumEmpty = false; } refreshHeight(); }, _list->lifetime()); } void MusicInner::refreshEmpty() { const auto savedMusic = &_controller->session().data().savedMusic(); const auto knownEmpty = savedMusic->countKnown(_peer->id); _empty = object_ptr( this, (!knownEmpty ? tr::lng_contacts_loading(tr::marked) : rpl::single( tr::lng_media_song_empty(tr::now, tr::marked))), st::giftListAbout); _empty->show(); _emptyLoading = !knownEmpty; resizeToWidth(width()); } void MusicInner::saveState(not_null memento) { _list->saveState(&memento->media()); } void MusicInner::restoreState(not_null memento) { _list->restoreState(&memento->media()); } rpl::producer MusicInner::selectedListValue() const { return _selectedLists.events_starting_with( _list->selectedListValue() ) | rpl::flatten_latest(); } void MusicInner::selectionAction(SelectionAction action) { _list->selectionAction(action); } int MusicInner::resizeGetHeight(int newWidth) { if (!newWidth) { return 0; } _inResize = true; auto guard = gsl::finally([this] { _inResize = false; }); if (_list) { _list->resizeToWidth(newWidth); } if (const auto empty = _empty.get()) { const auto margin = st::giftListAboutMargin; empty->resizeToWidth(newWidth - margin.left() - margin.right()); } return recountHeight(); } void MusicInner::refreshHeight() { if (_inResize) { return; } resize(width(), recountHeight()); } int MusicInner::recountHeight() { auto top = 0; auto listHeight = 0; if (_list) { _list->moveToLeft(0, top); listHeight = _list->heightNoMargins(); top += listHeight; } if (const auto empty = _empty.get()) { const auto margin = st::giftListAboutMargin; empty->moveToLeft(margin.left(), top + margin.top()); top += margin.top() + empty->height() + margin.bottom(); } if (_emptyLoading) { top = std::max(top, _lastNonLoadingHeight); } else { _lastNonLoadingHeight = top; } return top; } void MusicInner::setScrollHeightValue(rpl::producer value) { } rpl::producer MusicInner::scrollToRequests() const { return _scrollToRequests.events(); } MusicMemento::MusicMemento(not_null controller) : ContentMemento(MusicTag{ controller->musicPeer() }) , _media(controller) { } MusicMemento::MusicMemento(not_null peer) : ContentMemento(MusicTag{ peer }) , _media(peer, 0, Media::Type::MusicFile) { } MusicMemento::~MusicMemento() = default; Section MusicMemento::section() const { return Section(Section::Type::SavedMusic); } object_ptr MusicMemento::createWidget( QWidget *parent, not_null controller, const QRect &geometry) { auto result = object_ptr(parent, controller); result->setInternalState(geometry, this); return result; } MusicWidget::MusicWidget( QWidget *parent, not_null controller) : ContentWidget(parent, controller) { _inner = setInnerWidget(object_ptr(this, controller)); _inner->setScrollHeightValue(scrollHeightValue()); _inner->scrollToRequests( ) | rpl::on_next([this](Ui::ScrollToRequest request) { scrollTo(request); }, _inner->lifetime()); } void MusicWidget::setIsStackBottom(bool isStackBottom) { ContentWidget::setIsStackBottom(isStackBottom); _inner->setIsStackBottom(isStackBottom); } bool MusicWidget::showInternal(not_null memento) { if (!controller()->validateMementoPeer(memento)) { return false; } return true; } void MusicWidget::setInternalState( const QRect &geometry, not_null memento) { setGeometry(geometry); Ui::SendPendingMoveResizeEvents(this); restoreState(memento); } std::shared_ptr MusicWidget::doCreateMemento() { auto result = std::make_shared(controller()); saveState(result.get()); return result; } void MusicWidget::saveState(not_null memento) { memento->setScrollTop(scrollTopSave()); _inner->saveState(memento); } void MusicWidget::restoreState(not_null memento) { _inner->restoreState(memento); scrollTopRestore(memento->scrollTop()); } rpl::producer MusicWidget::selectedListValue() const { return _inner->selectedListValue(); } void MusicWidget::selectionAction(SelectionAction action) { _inner->selectionAction(action); } rpl::producer MusicWidget::title() { return controller()->key().musicPeer()->isSelf() ? tr::lng_media_saved_music_your() : tr::lng_media_saved_music_title(); } std::shared_ptr MakeMusic(not_null peer) { return std::make_shared( std::vector>( 1, std::make_shared(peer))); } } // namespace Info::Saved