From 598f01069eee4cff370238dc9f2eae1b79eca0ca Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 15 Sep 2021 14:03:21 +0200 Subject: [PATCH] Added non album tracks in the artist view. closes #165 --- approot/artist.xml | 6 +-- src/libs/database/impl/Artist.cpp | 35 ++++++++++-- src/libs/database/include/database/Artist.hpp | 3 +- src/lms/ui/explore/ArtistView.cpp | 54 +++++++++++++++---- src/lms/ui/explore/ArtistView.hpp | 41 +++++++------- src/test/database/Artist.cpp | 15 +++++- 6 files changed, 114 insertions(+), 40 deletions(-) diff --git a/approot/artist.xml b/approot/artist.xml index f1e839d8..79f7c3c9 100644 --- a/approot/artist.xml +++ b/approot/artist.xml @@ -15,11 +15,7 @@ ${} ${}
-
-
- ${tracks} -
-
+ ${tracks}
${
}
diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/database/impl/Artist.cpp index 91925b58..36c6dede 100644 --- a/src/libs/database/impl/Artist.cpp +++ b/src/libs/database/impl/Artist.cpp @@ -465,12 +465,39 @@ Artist::getTracks(std::optional linkType) const } std::vector> -Artist::getNonReleaseTracks(std::optional linkType) const +Artist::getNonReleaseTracks(std::optional linkType, std::optional range, bool& moreResults) const { assert(self()); assert(IdIsValid(self()->id())); assert(session()); + auto query {session()->query>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id") + .where("a.id = ?").bind(self()->id()) + .where("t.release_id is NULL") + .orderBy("t.name") + .limit(range ? static_cast(range->limit) + 1 : -1) + .offset(range ? static_cast(range->offset) : -1)}; + + if (linkType) + query.where("t_a_l.type = ?").bind(*linkType); + + Wt::Dbo::collection tracks {query.resultList()}; + + auto res {std::vector(tracks.begin(), tracks.end())}; + if (range && res.size() == static_cast(range->limit) + 1) + { + moreResults = true; + res.pop_back(); + } + else + moreResults = false; + + return res; +} + +bool +Artist::hasNonReleaseTracks(std::optional linkType) const +{ auto query {session()->query>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id") .where("a.id = ?").bind(self()->id()) .where("t.release_id is NULL") @@ -479,12 +506,10 @@ Artist::getNonReleaseTracks(std::optional linkType) const if (linkType) query.where("t_a_l.type = ?").bind(*linkType); - Wt::Dbo::collection> tracks {query.resultList()}; - - return std::vector>(tracks.begin(), tracks.end()); + Wt::Dbo::collection tracks {query.resultList()}; + return !tracks.empty(); } - std::vector> Artist::getRandomTracks(std::optional count) const { diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/database/include/database/Artist.hpp index 824243f8..a9ef7c81 100644 --- a/src/libs/database/include/database/Artist.hpp +++ b/src/libs/database/include/database/Artist.hpp @@ -104,7 +104,8 @@ class Artist : public Wt::Dbo::Dbo std::vector> getReleases(const std::set& clusterIds = {}) const; // if non empty, get the releases that match all these clusters std::size_t getReleaseCount() const; std::vector> getTracks(std::optional linkType = {}) const; - std::vector> getNonReleaseTracks(std::optional linkType = {}) const; + bool hasNonReleaseTracks(std::optional linkType = std::nullopt) const; + std::vector> getNonReleaseTracks(std::optional linkType, std::optional range, bool& moreResults) const; std::vector> getRandomTracks(std::optional count) const; // No artistLinkTypes means get them all diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 1a1be090..79941fc1 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -35,6 +35,7 @@ #include "utils/Logger.hpp" #include "utils/String.hpp" +#include "common/InfiniteScrollingContainer.hpp" #include "resource/DownloadResource.hpp" #include "ArtistListHelpers.hpp" #include "Filters.hpp" @@ -93,6 +94,8 @@ Artist::refreshView() return; clear(); + _artistId = {}; + _trackContainer = nullptr; const auto artistId {extractArtistIdFromInternalPath()}; if (!artistId) @@ -109,6 +112,8 @@ Artist::refreshView() if (!artist) throw ArtistNotFoundException {}; + _artistId = *artistId; + refreshReleases(artist); refreshNonReleaseTracks(artist); refreshLinks(artist); @@ -140,7 +145,7 @@ Artist::refreshView() playBtn->clicked().connect([=] { - artistsAction.emit(PlayQueueAction::Play, {*artistId}); + artistsAction.emit(PlayQueueAction::Play, {_artistId}); }); } @@ -154,12 +159,12 @@ Artist::refreshView() popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled")) ->triggered().connect(this, [=] { - artistsAction.emit(PlayQueueAction::PlayShuffled, {*artistId}); + artistsAction.emit(PlayQueueAction::PlayShuffled, {_artistId}); }); popup->addItem(Wt::WString::tr("Lms.Explore.play-last")) ->triggered().connect(this, [=] { - artistsAction.emit(PlayQueueAction::PlayLast, {*artistId}); + artistsAction.emit(PlayQueueAction::PlayLast, {_artistId}); }); bool isStarred {}; @@ -210,17 +215,17 @@ Artist::refreshReleases(const Wt::Dbo::ptr& artist) void Artist::refreshNonReleaseTracks(const Wt::Dbo::ptr& artist) { - const auto tracks {artist->getNonReleaseTracks()}; - if (tracks.empty()) + if (!artist->hasNonReleaseTracks()) return; setCondition("if-has-non-release-track", true); - - Wt::WContainerWidget* tracksContainer = bindNew("tracks"); - for (const Track::pointer& track : tracks) + _trackContainer = bindNew("tracks", Wt::WString::tr("Lms.Explore.Tracks.template.container")); + _trackContainer->onRequestElements.connect(this, [this] { - tracksContainer->addWidget(TrackListHelpers::createEntry(track, tracksAction)); - } + addSomeNonReleaseTracks(); + }); + + addSomeNonReleaseTracks(); } void @@ -257,5 +262,34 @@ Artist::refreshLinks(const Database::Artist::pointer& artist) } } +void +Artist::addSomeNonReleaseTracks() +{ + bool moreResults {}; + + { + auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + + const Database::Artist::pointer artist {Database::Artist::getById(LmsApp->getDbSession(), _artistId)}; + if (!artist) + return; + + const auto tracks {artist->getNonReleaseTracks(std::nullopt, Database::Range {static_cast(_trackContainer->getCount()), _tracksBatchSize}, moreResults)}; + + for (const auto& track : tracks) + { + if (_trackContainer->getCount() == _tracksMaxCount) + { + moreResults = false; + break; + } + + _trackContainer->add(TrackListHelpers::createEntry(track, tracksAction)); + } + } + + _trackContainer->setHasMore(moreResults); +} + } // namespace UserInterface diff --git a/src/lms/ui/explore/ArtistView.hpp b/src/lms/ui/explore/ArtistView.hpp index 3f17238b..2d5ce695 100644 --- a/src/lms/ui/explore/ArtistView.hpp +++ b/src/lms/ui/explore/ArtistView.hpp @@ -34,29 +34,34 @@ namespace Database class Release; } -namespace UserInterface { - -class Filters; - -class Artist : public Wt::WTemplate +namespace UserInterface { - public: - Artist(Filters* filters); - PlayQueueActionSignal artistsAction; - PlayQueueActionSignal tracksAction; + class Filters; + class InfiniteScrollingContainer; - private: - void refreshView(); - void refreshReleases(const Wt::Dbo::ptr& artist); - void refreshNonReleaseTracks(const Wt::Dbo::ptr& artist); - void refreshSimilarArtists(const std::unordered_set& similarArtistsId); - void refreshLinks(const Wt::Dbo::ptr& artist); + class Artist : public Wt::WTemplate + { + public: + Artist(Filters* filters); - std::unique_ptr createRelease(const Wt::Dbo::ptr& artist, const Wt::Dbo::ptr& release); + PlayQueueActionSignal artistsAction; + PlayQueueActionSignal tracksAction; - Filters* _filters {}; -}; + private: + void refreshView(); + void refreshReleases(const Wt::Dbo::ptr& artist); + void refreshNonReleaseTracks(const Wt::Dbo::ptr& artist); + void refreshSimilarArtists(const std::unordered_set& similarArtistsId); + void refreshLinks(const Wt::Dbo::ptr& artist); + void addSomeNonReleaseTracks(); + static constexpr std::size_t _tracksBatchSize {6}; + static constexpr std::size_t _tracksMaxCount {160}; + + Filters* _filters {}; + InfiniteScrollingContainer* _trackContainer {}; + Database::IdType _artistId {}; + }; } // namespace UserInterface diff --git a/src/test/database/Artist.cpp b/src/test/database/Artist.cpp index d32d7c0e..efb43d68 100644 --- a/src/test/database/Artist.cpp +++ b/src/test/database/Artist.cpp @@ -318,6 +318,16 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) ScopedTrack track1 {session, "MyTrack1"}; ScopedTrack track2 {session, "MyTrack2"}; ScopedRelease release{session, "MyRelease"}; + + { + auto transaction {session.createSharedTransaction()}; + EXPECT_EQ(artist->hasNonReleaseTracks(std::nullopt), false); + + bool moreResults; + const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )}; + EXPECT_EQ(tracks.size(), 0); + } + { auto transaction {session.createUniqueTransaction()}; @@ -331,7 +341,10 @@ TEST_F(DatabaseFixture, SingleArtistNonReleaseTracks) { auto transaction {session.createSharedTransaction()}; - const auto tracks {artist->getNonReleaseTracks()}; + bool moreResults; + const auto tracks {artist->getNonReleaseTracks(std::nullopt, std::nullopt, moreResults )}; + EXPECT_EQ(artist->hasNonReleaseTracks(std::nullopt), true); + EXPECT_EQ(moreResults, false); ASSERT_EQ(tracks.size(), 1); EXPECT_EQ(tracks.front().id(), track2.getId()); }