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 @@
${}
${}
${}
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());
}