Removed the dedicated search view, as it is actually too limiting. Added a search field in the album/artists/tracks views. This makes search now work together with sort mode
This commit is contained in:
+5
-2
@@ -4,8 +4,11 @@
|
|||||||
|
|
||||||
<message id="Lms.Explore.Artists.template">
|
<message id="Lms.Explore.Artists.template">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
${link-type}
|
${link-type class="me-1"}
|
||||||
${sort-mode}
|
<div class="d-flex">
|
||||||
|
${search class="form-control me-1" type="search"}
|
||||||
|
${sort-mode}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${artists}
|
${artists}
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -14,9 +14,6 @@
|
|||||||
<message id="Lms.main.template.navbar">
|
<message id="Lms.main.template.navbar">
|
||||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-3">
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-3">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="d-flex">
|
|
||||||
${search class="form-control" type="search"}
|
|
||||||
</div>
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<message id="Lms.Explore.Releases.template">
|
<message id="Lms.Explore.Releases.template">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<div class="btn-group">
|
<div class="btn-group me-1">
|
||||||
${play-btn class="btn btn-primary"}
|
${play-btn class="btn btn-primary"}
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
@@ -13,7 +13,10 @@
|
|||||||
<li>${play-shuffled class="dropdown-item"}</li>
|
<li>${play-shuffled class="dropdown-item"}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
${sort-mode}
|
<div class="d-flex">
|
||||||
|
${search class="form-control me-1" type="search"}
|
||||||
|
${sort-mode}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${releases}
|
${releases}
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
|
||||||
<!--FORMS message blocks-->
|
|
||||||
|
|
||||||
<message id="Lms.Explore.Search.template">
|
|
||||||
<div class="mb-3">
|
|
||||||
<ul class="nav nav-pills mb-3">
|
|
||||||
<li class="nav-item">
|
|
||||||
${releases class="nav-link"}
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
${artists class="nav-link"}
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
${tracks class="nav-link"}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
${results}
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="Lms.Explore.Search.template.artists">
|
|
||||||
<div class="mb-3">
|
|
||||||
${link-type}
|
|
||||||
</div>
|
|
||||||
${artists}
|
|
||||||
</message>
|
|
||||||
|
|
||||||
</messages>
|
|
||||||
+5
-2
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<message id="Lms.Explore.Tracks.template">
|
<message id="Lms.Explore.Tracks.template">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<div class="btn-group">
|
<div class="btn-group me-1">
|
||||||
${play-btn class="btn btn-primary"}
|
${play-btn class="btn btn-primary"}
|
||||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
@@ -14,7 +14,10 @@
|
|||||||
<li>${play-shuffled class="dropdown-item"}</li>
|
<li>${play-shuffled class="dropdown-item"}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
${sort-mode}
|
<div class="d-flex">
|
||||||
|
${search class="form-control me-1" type="search"}
|
||||||
|
${sort-mode}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${tracks}
|
${tracks}
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -32,9 +32,8 @@ namespace lms::db
|
|||||||
Wt::Dbo::Query<ArtistId> createArtistsQuery(Session& session, const Listen::ArtistStatsFindParameters& params)
|
Wt::Dbo::Query<ArtistId> createArtistsQuery(Session& session, const Listen::ArtistStatsFindParameters& params)
|
||||||
{
|
{
|
||||||
auto query{ session.getDboSession()->query<ArtistId>("SELECT a.id from artist a")
|
auto query{ session.getDboSession()->query<ArtistId>("SELECT a.id from artist a")
|
||||||
.join("track t ON t.id = t_a_l.track_id")
|
|
||||||
.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id")
|
.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id")
|
||||||
.join("listen l ON l.track_id = t.id") };
|
.join("listen l ON l.track_id = t_a_l.track_id") };
|
||||||
|
|
||||||
if (params.user.isValid())
|
if (params.user.isValid())
|
||||||
query.where("l.user_id = ?").bind(params.user);
|
query.where("l.user_id = ?").bind(params.user);
|
||||||
@@ -45,7 +44,10 @@ namespace lms::db
|
|||||||
assert(!params.artist.isValid()); // poor check
|
assert(!params.artist.isValid()); // poor check
|
||||||
|
|
||||||
if (params.library.isValid())
|
if (params.library.isValid())
|
||||||
|
{
|
||||||
|
query.join("track t ON t.id = t_a_l.track_id");
|
||||||
query.where("t.media_library_id = ?").bind(params.library);
|
query.where("t.media_library_id = ?").bind(params.library);
|
||||||
|
}
|
||||||
|
|
||||||
if (params.linkType)
|
if (params.linkType)
|
||||||
query.where("t_a_l.type = ?").bind(*params.linkType);
|
query.where("t_a_l.type = ?").bind(*params.linkType);
|
||||||
@@ -69,6 +71,26 @@ namespace lms::db
|
|||||||
query.where(oss.str());
|
query.where(oss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!params.keywords.empty())
|
||||||
|
{
|
||||||
|
std::vector<std::string> clauses;
|
||||||
|
std::vector<std::string> sortClauses;
|
||||||
|
|
||||||
|
for (const std::string_view keyword : params.keywords)
|
||||||
|
{
|
||||||
|
clauses.push_back("a.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
|
||||||
|
query.bind("%" + utils::escapeLikeKeyword(keyword) + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::string_view keyword : params.keywords)
|
||||||
|
{
|
||||||
|
sortClauses.push_back("a.sort_name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
|
||||||
|
query.bind("%" + utils::escapeLikeKeyword(keyword) + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
query.where("(" + core::stringUtils::joinStrings(clauses, " AND ") + ") OR (" + core::stringUtils::joinStrings(sortClauses, " AND ") + ")");
|
||||||
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +136,9 @@ namespace lms::db
|
|||||||
query.where(oss.str());
|
query.where(oss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (std::string_view keyword : params.keywords)
|
||||||
|
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +182,9 @@ namespace lms::db
|
|||||||
query.where(oss.str());
|
query.where(oss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (std::string_view keyword : params.keywords)
|
||||||
|
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ namespace lms::db
|
|||||||
UserId user;
|
UserId user;
|
||||||
std::optional<ScrobblingBackend> backend;
|
std::optional<ScrobblingBackend> backend;
|
||||||
std::vector<ClusterId> clusters; // if non empty, entities that belong to these clusters
|
std::vector<ClusterId> clusters; // if non empty, entities that belong to these clusters
|
||||||
|
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords
|
||||||
std::optional<Range> range;
|
std::optional<Range> range;
|
||||||
ArtistId artist; // if set, matching this artist
|
ArtistId artist; // if set, matching this artist
|
||||||
MediaLibraryId library;
|
MediaLibraryId library;
|
||||||
@@ -77,6 +78,7 @@ namespace lms::db
|
|||||||
StatsFindParameters& setUser(UserId _user) { user = _user; return *this; }
|
StatsFindParameters& setUser(UserId _user) { user = _user; return *this; }
|
||||||
StatsFindParameters& setScrobblingBackend(std::optional<ScrobblingBackend> _backend) { backend = _backend; return *this; }
|
StatsFindParameters& setScrobblingBackend(std::optional<ScrobblingBackend> _backend) { backend = _backend; return *this; }
|
||||||
StatsFindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
StatsFindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||||
|
StatsFindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||||
StatsFindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
StatsFindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
|
||||||
StatsFindParameters& setArtist(ArtistId _artist) { artist = _artist; return *this; }
|
StatsFindParameters& setArtist(ArtistId _artist) { artist = _artist; return *this; }
|
||||||
StatsFindParameters& setMediaLibrary(MediaLibraryId _library) { library = _library; return *this; }
|
StatsFindParameters& setMediaLibrary(MediaLibraryId _library) { library = _library; return *this; }
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ namespace lms::db
|
|||||||
UserId starringUser; // only releases starred by this user
|
UserId starringUser; // only releases starred by this user
|
||||||
std::optional<FeedbackBackend> feedbackBackend; // and for this backend
|
std::optional<FeedbackBackend> feedbackBackend; // and for this backend
|
||||||
ArtistId artist; // only releases that involved this user
|
ArtistId artist; // only releases that involved this user
|
||||||
core::EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
|
core::EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
|
||||||
core::EnumSet<TrackArtistLinkType> excludedTrackArtistLinkTypes; // but not for these link types
|
core::EnumSet<TrackArtistLinkType> excludedTrackArtistLinkTypes; // but not for these link types
|
||||||
std::string releaseType; // If set, albums that has this release type
|
std::string releaseType; // If set, albums that has this release type
|
||||||
MediaLibraryId mediaLibrary; // If set, releases that has at least a track in this library
|
MediaLibraryId mediaLibrary; // If set, releases that has at least a track in this library
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ namespace lms::feedback
|
|||||||
Artist::FindParameters searchParams;
|
Artist::FindParameters searchParams;
|
||||||
searchParams.setStarringUser(params.user, *backend);
|
searchParams.setStarringUser(params.user, *backend);
|
||||||
searchParams.setClusters(params.clusters);
|
searchParams.setClusters(params.clusters);
|
||||||
|
searchParams.setKeywords(params.keywords);
|
||||||
searchParams.setLinkType(params.linkType);
|
searchParams.setLinkType(params.linkType);
|
||||||
searchParams.setSortMethod(params.sortMethod);
|
searchParams.setSortMethod(params.sortMethod);
|
||||||
searchParams.setRange(params.range);
|
searchParams.setRange(params.range);
|
||||||
@@ -136,6 +137,7 @@ namespace lms::feedback
|
|||||||
Release::FindParameters searchParams;
|
Release::FindParameters searchParams;
|
||||||
searchParams.setStarringUser(params.user, *backend);
|
searchParams.setStarringUser(params.user, *backend);
|
||||||
searchParams.setClusters(params.clusters);
|
searchParams.setClusters(params.clusters);
|
||||||
|
searchParams.setKeywords(params.keywords);
|
||||||
searchParams.setSortMethod(ReleaseSortMethod::StarredDateDesc);
|
searchParams.setSortMethod(ReleaseSortMethod::StarredDateDesc);
|
||||||
searchParams.setRange(params.range);
|
searchParams.setRange(params.range);
|
||||||
searchParams.setMediaLibrary(params.library);
|
searchParams.setMediaLibrary(params.library);
|
||||||
@@ -175,6 +177,7 @@ namespace lms::feedback
|
|||||||
Track::FindParameters searchParams;
|
Track::FindParameters searchParams;
|
||||||
searchParams.setStarringUser(params.user, *backend);
|
searchParams.setStarringUser(params.user, *backend);
|
||||||
searchParams.setClusters(params.clusters);
|
searchParams.setClusters(params.clusters);
|
||||||
|
searchParams.setKeywords(params.keywords);
|
||||||
searchParams.setSortMethod(TrackSortMethod::StarredDateDesc);
|
searchParams.setSortMethod(TrackSortMethod::StarredDateDesc);
|
||||||
searchParams.setRange(params.range);
|
searchParams.setRange(params.range);
|
||||||
searchParams.setMediaLibrary(params.library);
|
searchParams.setMediaLibrary(params.library);
|
||||||
|
|||||||
@@ -54,11 +54,13 @@ namespace lms::feedback
|
|||||||
{
|
{
|
||||||
db::UserId user;
|
db::UserId user;
|
||||||
std::vector<db::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
std::vector<db::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||||
|
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords
|
||||||
std::optional<db::Range> range;
|
std::optional<db::Range> range;
|
||||||
db::MediaLibraryId library;
|
db::MediaLibraryId library;
|
||||||
|
|
||||||
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
||||||
FindParameters& setClusters(std::span<const db::ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
FindParameters& setClusters(std::span<const db::ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
|
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||||
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
||||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace lms::scrobbling
|
|||||||
db::Listen::StatsFindParameters listenFindParams;
|
db::Listen::StatsFindParameters listenFindParams;
|
||||||
listenFindParams.setUser(params.user);
|
listenFindParams.setUser(params.user);
|
||||||
listenFindParams.setClusters(params.clusters);
|
listenFindParams.setClusters(params.clusters);
|
||||||
|
listenFindParams.setKeywords(params.keywords);
|
||||||
listenFindParams.setRange(params.range);
|
listenFindParams.setRange(params.range);
|
||||||
listenFindParams.setMediaLibrary(params.library);
|
listenFindParams.setMediaLibrary(params.library);
|
||||||
listenFindParams.setArtist(params.artist);
|
listenFindParams.setArtist(params.artist);
|
||||||
|
|||||||
@@ -59,14 +59,16 @@ namespace lms::scrobbling
|
|||||||
|
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
{
|
{
|
||||||
db::UserId user;
|
db::UserId user;
|
||||||
std::vector<db::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
std::vector<db::ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||||
std::optional<db::Range> range;
|
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords
|
||||||
db::MediaLibraryId library; // if set, match this library
|
std::optional<db::Range> range;
|
||||||
db::ArtistId artist; // if set, match this artist
|
db::MediaLibraryId library; // if set, match this library
|
||||||
|
db::ArtistId artist; // if set, match this artist
|
||||||
|
|
||||||
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
FindParameters& setUser(const db::UserId _user) { user = _user; return *this; }
|
||||||
FindParameters& setClusters(std::span<const db::ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
FindParameters& setClusters(std::span<const db::ClusterId> _clusters) { clusters.assign(std::cbegin(_clusters), std::cend(_clusters)); return *this; }
|
||||||
|
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||||
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
||||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||||
FindParameters& setArtist(db::ArtistId _artist) { artist = _artist; return *this; }
|
FindParameters& setArtist(db::ArtistId _artist) { artist = _artist; return *this; }
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ add_executable(lms
|
|||||||
ui/explore/ReleasesView.cpp
|
ui/explore/ReleasesView.cpp
|
||||||
ui/explore/ReleaseView.cpp
|
ui/explore/ReleaseView.cpp
|
||||||
ui/explore/ReleaseTypes.cpp
|
ui/explore/ReleaseTypes.cpp
|
||||||
ui/explore/SearchView.cpp
|
|
||||||
ui/explore/SortModeSelector.cpp
|
ui/explore/SortModeSelector.cpp
|
||||||
ui/explore/TrackArtistLinkTypeSelector.cpp
|
ui/explore/TrackArtistLinkTypeSelector.cpp
|
||||||
ui/explore/TrackCollector.cpp
|
ui/explore/TrackCollector.cpp
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ namespace lms::ui
|
|||||||
res->use(appRoot + "playqueue");
|
res->use(appRoot + "playqueue");
|
||||||
res->use(appRoot + "release");
|
res->use(appRoot + "release");
|
||||||
res->use(appRoot + "releases");
|
res->use(appRoot + "releases");
|
||||||
res->use(appRoot + "search");
|
|
||||||
res->use(appRoot + "settings");
|
res->use(appRoot + "settings");
|
||||||
res->use(appRoot + "tracklist");
|
res->use(appRoot + "tracklist");
|
||||||
res->use(appRoot + "tracklists");
|
res->use(appRoot + "tracklists");
|
||||||
@@ -139,7 +138,6 @@ namespace lms::ui
|
|||||||
{ "/artist", IdxExplore, false, std::nullopt },
|
{ "/artist", IdxExplore, false, std::nullopt },
|
||||||
{ "/releases", IdxExplore, false, Wt::WString::tr("Lms.Explore.releases") },
|
{ "/releases", IdxExplore, false, Wt::WString::tr("Lms.Explore.releases") },
|
||||||
{ "/release", IdxExplore, false, std::nullopt },
|
{ "/release", IdxExplore, false, std::nullopt },
|
||||||
{ "/search", IdxExplore, false, Wt::WString::tr("Lms.Explore.search") },
|
|
||||||
{ "/tracks", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracks") },
|
{ "/tracks", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracks") },
|
||||||
{ "/tracklists", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracklists") },
|
{ "/tracklists", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracklists") },
|
||||||
{ "/tracklist", IdxExplore, false, std::nullopt },
|
{ "/tracklist", IdxExplore, false, std::nullopt },
|
||||||
@@ -435,9 +433,6 @@ namespace lms::ui
|
|||||||
logout->clicked().connect(this, &LmsApplication::logoutUser);
|
logout->clicked().connect(this, &LmsApplication::logoutUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
Wt::WLineEdit* searchEdit{ navbar->bindNew<Wt::WLineEdit>("search") };
|
|
||||||
searchEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder"));
|
|
||||||
|
|
||||||
if (LmsApp->getUserType() == db::UserType::ADMIN)
|
if (LmsApp->getUserType() == db::UserType::ADMIN)
|
||||||
{
|
{
|
||||||
navbar->setCondition("if-is-admin", true);
|
navbar->setCondition("if-is-admin", true);
|
||||||
@@ -462,17 +457,6 @@ namespace lms::ui
|
|||||||
_playQueue = mainStack->addWidget(std::move(playQueue));
|
_playQueue = mainStack->addWidget(std::move(playQueue));
|
||||||
mainStack->addNew<SettingsView>();
|
mainStack->addNew<SettingsView>();
|
||||||
|
|
||||||
searchEdit->enterPressed().connect([this]
|
|
||||||
{
|
|
||||||
setInternalPath("/search", true);
|
|
||||||
});
|
|
||||||
|
|
||||||
searchEdit->textInput().connect([this, explore, searchEdit]
|
|
||||||
{
|
|
||||||
setInternalPath("/search", true);
|
|
||||||
explore->search(searchEdit->text());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Admin stuff
|
// Admin stuff
|
||||||
if (getUserType() == db::UserType::ADMIN)
|
if (getUserType() == db::UserType::ADMIN)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ namespace lms::ui
|
|||||||
feedback::IFeedbackService::ArtistFindParameters params;
|
feedback::IFeedbackService::ArtistFindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(filters.getClusters());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setMediaLibrary(filters.getMediaLibrary());
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::StarredDateDesc);
|
params.setSortMethod(ArtistSortMethod::StarredDateDesc);
|
||||||
@@ -67,6 +68,7 @@ namespace lms::ui
|
|||||||
scrobbling::IScrobblingService::ArtistFindParameters params;
|
scrobbling::IScrobblingService::ArtistFindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(filters.getClusters());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setMediaLibrary(filters.getMediaLibrary());
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
@@ -80,6 +82,7 @@ namespace lms::ui
|
|||||||
scrobbling::IScrobblingService::ArtistFindParameters params;
|
scrobbling::IScrobblingService::ArtistFindParameters params;
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(filters.getClusters());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setMediaLibrary(filters.getMediaLibrary());
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
@@ -92,6 +95,7 @@ namespace lms::ui
|
|||||||
{
|
{
|
||||||
Artist::FindParameters params;
|
Artist::FindParameters params;
|
||||||
params.setClusters(filters.getClusters());
|
params.setClusters(filters.getClusters());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setMediaLibrary(filters.getMediaLibrary());
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::LastWritten);
|
params.setSortMethod(ArtistSortMethod::LastWritten);
|
||||||
@@ -104,27 +108,12 @@ namespace lms::ui
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Mode::Search:
|
|
||||||
{
|
|
||||||
Artist::FindParameters params;
|
|
||||||
params.setClusters(filters.getClusters());
|
|
||||||
params.setMediaLibrary(filters.getMediaLibrary());
|
|
||||||
params.setKeywords(getSearchKeywords());
|
|
||||||
params.setLinkType(_linkType);
|
|
||||||
params.setRange(range);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
|
||||||
artists = Artist::findIds(LmsApp->getDbSession(), params);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Mode::All:
|
case Mode::All:
|
||||||
{
|
{
|
||||||
Artist::FindParameters params;
|
Artist::FindParameters params;
|
||||||
params.setClusters(filters.getClusters());
|
params.setClusters(filters.getClusters());
|
||||||
params.setMediaLibrary(filters.getMediaLibrary());
|
params.setMediaLibrary(filters.getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::SortName);
|
params.setSortMethod(ArtistSortMethod::SortName);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
@@ -152,6 +141,7 @@ namespace lms::ui
|
|||||||
Artist::FindParameters params;
|
Artist::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setLinkType(_linkType);
|
params.setLinkType(_linkType);
|
||||||
params.setSortMethod(ArtistSortMethod::Random);
|
params.setSortMethod(ArtistSortMethod::Random);
|
||||||
params.setRange(Range{ 0, getMaxCount() });
|
params.setRange(Range{ 0, getMaxCount() });
|
||||||
|
|||||||
@@ -38,10 +38,18 @@ namespace lms::ui
|
|||||||
using namespace db;
|
using namespace db;
|
||||||
|
|
||||||
Artists::Artists(Filters& filters)
|
Artists::Artists(Filters& filters)
|
||||||
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.Artists.template") }
|
: Template{ Wt::WString::tr("Lms.Explore.Artists.template") }
|
||||||
, _artistCollector{ filters, _defaultSortMode, _maxCount }
|
, _artistCollector{ filters, _defaultSortMode, _maxCount }
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
|
|
||||||
|
Wt::WLineEdit* searEdit{ bindNew<Wt::WLineEdit>("search") };
|
||||||
|
searEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder"));
|
||||||
|
searEdit->textInput().connect([this, searEdit]
|
||||||
|
{
|
||||||
|
refreshView(searEdit->text());
|
||||||
|
});
|
||||||
|
|
||||||
SortModeSelector* sortModeSelector{ bindNew<SortModeSelector>("sort-mode", _defaultSortMode) };
|
SortModeSelector* sortModeSelector{ bindNew<SortModeSelector>("sort-mode", _defaultSortMode) };
|
||||||
sortModeSelector->sortModeChanged.connect([this](ArtistCollector::Mode sortMode)
|
sortModeSelector->sortModeChanged.connect([this](ArtistCollector::Mode sortMode)
|
||||||
@@ -87,6 +95,12 @@ namespace lms::ui
|
|||||||
refreshView();
|
refreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Artists::refreshView(const Wt::WString& searchText)
|
||||||
|
{
|
||||||
|
_artistCollector.setSearch(searchText.toUTF8());
|
||||||
|
refreshView();
|
||||||
|
}
|
||||||
|
|
||||||
void Artists::addSome()
|
void Artists::addSome()
|
||||||
{
|
{
|
||||||
const auto artistIds{ _artistCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}) };
|
const auto artistIds{ _artistCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}) };
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "common/Template.hpp"
|
||||||
#include "ArtistCollector.hpp"
|
#include "ArtistCollector.hpp"
|
||||||
|
|
||||||
namespace lms::ui
|
namespace lms::ui
|
||||||
@@ -33,7 +34,7 @@ namespace lms::ui
|
|||||||
class Filters;
|
class Filters;
|
||||||
class InfiniteScrollingContainer;
|
class InfiniteScrollingContainer;
|
||||||
|
|
||||||
class Artists : public Wt::WTemplate
|
class Artists : public Template
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Artists(Filters& filters);
|
Artists(Filters& filters);
|
||||||
@@ -42,6 +43,7 @@ namespace lms::ui
|
|||||||
void refreshView();
|
void refreshView();
|
||||||
void refreshView(ArtistCollector::Mode mode);
|
void refreshView(ArtistCollector::Mode mode);
|
||||||
void refreshView(std::optional<db::TrackArtistLinkType> linkType);
|
void refreshView(std::optional<db::TrackArtistLinkType> linkType);
|
||||||
|
void refreshView(const Wt::WString& searchText);
|
||||||
void addSome();
|
void addSome();
|
||||||
|
|
||||||
static constexpr std::size_t _batchSize{ 30 };
|
static constexpr std::size_t _batchSize{ 30 };
|
||||||
|
|||||||
@@ -59,7 +59,10 @@ namespace lms::ui
|
|||||||
void DatabaseCollectorBase::setSearch(std::string_view searchText)
|
void DatabaseCollectorBase::setSearch(std::string_view searchText)
|
||||||
{
|
{
|
||||||
_searchText = searchText;
|
_searchText = searchText;
|
||||||
_searchKeywords = core::stringUtils::splitString(_searchText, ' ');
|
if (!searchText.empty())
|
||||||
|
_searchKeywords = core::stringUtils::splitString(_searchText, ' ');
|
||||||
|
else
|
||||||
|
_searchKeywords.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ns UserInterface
|
} // ns UserInterface
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ namespace lms::ui
|
|||||||
RecentlyPlayed,
|
RecentlyPlayed,
|
||||||
RecentlyAdded,
|
RecentlyAdded,
|
||||||
MostPlayed,
|
MostPlayed,
|
||||||
Search,
|
|
||||||
All
|
All
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
#include "ReleasesView.hpp"
|
#include "ReleasesView.hpp"
|
||||||
#include "ReleaseView.hpp"
|
#include "ReleaseView.hpp"
|
||||||
#include "SearchView.hpp"
|
|
||||||
#include "TrackListView.hpp"
|
#include "TrackListView.hpp"
|
||||||
#include "TrackListsView.hpp"
|
#include "TrackListsView.hpp"
|
||||||
#include "TracksView.hpp"
|
#include "TracksView.hpp"
|
||||||
@@ -46,7 +45,6 @@ namespace lms::ui
|
|||||||
IdxTrackList,
|
IdxTrackList,
|
||||||
IdxReleases,
|
IdxReleases,
|
||||||
IdxRelease,
|
IdxRelease,
|
||||||
IdxSearch,
|
|
||||||
IdxTracks,
|
IdxTracks,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,7 +56,6 @@ namespace lms::ui
|
|||||||
{ "/tracklist", IdxTrackList },
|
{ "/tracklist", IdxTrackList },
|
||||||
{ "/releases", IdxReleases },
|
{ "/releases", IdxReleases },
|
||||||
{ "/release", IdxRelease },
|
{ "/release", IdxRelease },
|
||||||
{ "/search", IdxSearch },
|
|
||||||
{ "/tracks", IdxTracks },
|
{ "/tracks", IdxTracks },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,10 +100,6 @@ namespace lms::ui
|
|||||||
auto release = std::make_unique<Release>(filters, _playQueueController);
|
auto release = std::make_unique<Release>(filters, _playQueueController);
|
||||||
contentsStack->addWidget(std::move(release));
|
contentsStack->addWidget(std::move(release));
|
||||||
|
|
||||||
auto search = std::make_unique<SearchView>(filters, _playQueueController);
|
|
||||||
_search = search.get();
|
|
||||||
contentsStack->addWidget(std::move(search));
|
|
||||||
|
|
||||||
auto tracks = std::make_unique<Tracks>(filters, _playQueueController);
|
auto tracks = std::make_unique<Tracks>(filters, _playQueueController);
|
||||||
contentsStack->addWidget(std::move(tracks));
|
contentsStack->addWidget(std::move(tracks));
|
||||||
|
|
||||||
@@ -117,9 +110,4 @@ namespace lms::ui
|
|||||||
|
|
||||||
handleContentsPathChange(contentsStack);
|
handleContentsPathChange(contentsStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Explore::search(const Wt::WString& searchText)
|
|
||||||
{
|
|
||||||
_search->refreshView(searchText);
|
|
||||||
}
|
|
||||||
} // namespace lms::ui
|
} // namespace lms::ui
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ namespace lms::ui
|
|||||||
public:
|
public:
|
||||||
Explore(Filters& filters, PlayQueue& playQueue);
|
Explore(Filters& filters, PlayQueue& playQueue);
|
||||||
|
|
||||||
void search(const Wt::WString& searchText);
|
|
||||||
PlayQueueController& getPlayQueueController() { return _playQueueController; }
|
PlayQueueController& getPlayQueueController() { return _playQueueController; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace lms::ui
|
|||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
releases = feedbackService.findStarredReleases(params);
|
releases = feedbackService.findStarredReleases(params);
|
||||||
break;
|
break;
|
||||||
@@ -64,6 +65,7 @@ namespace lms::ui
|
|||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
releases = scrobblingService.getRecentReleases(params);
|
releases = scrobblingService.getRecentReleases(params);
|
||||||
@@ -76,6 +78,7 @@ namespace lms::ui
|
|||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
releases = scrobblingService.getTopReleases(params);
|
releases = scrobblingService.getTopReleases(params);
|
||||||
@@ -83,26 +86,12 @@ namespace lms::ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Mode::RecentlyAdded:
|
case Mode::RecentlyAdded:
|
||||||
{
|
|
||||||
Release::FindParameters params;
|
|
||||||
params.setClusters(getFilters().getClusters());
|
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
|
||||||
params.setSortMethod(ReleaseSortMethod::LastWritten);
|
|
||||||
params.setRange(range);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
|
||||||
releases = Release::findIds(LmsApp->getDbSession(), params);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Mode::Search:
|
|
||||||
{
|
{
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setKeywords(getSearchKeywords());
|
params.setKeywords(getSearchKeywords());
|
||||||
|
params.setSortMethod(ReleaseSortMethod::LastWritten);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -118,6 +107,7 @@ namespace lms::ui
|
|||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setSortMethod(ReleaseSortMethod::Name);
|
params.setSortMethod(ReleaseSortMethod::Name);
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -143,6 +133,7 @@ namespace lms::ui
|
|||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setSortMethod(ReleaseSortMethod::Random);
|
params.setSortMethod(ReleaseSortMethod::Random);
|
||||||
params.setRange(Range{ 0, getMaxCount() });
|
params.setRange(Range{ 0, getMaxCount() });
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "ReleasesView.hpp"
|
#include "ReleasesView.hpp"
|
||||||
|
|
||||||
|
#include <Wt/WLineEdit.h>
|
||||||
#include <Wt/WPushButton.h>
|
#include <Wt/WPushButton.h>
|
||||||
|
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
@@ -44,6 +45,13 @@ namespace lms::ui
|
|||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
|
|
||||||
|
Wt::WLineEdit* searEdit{ bindNew<Wt::WLineEdit>("search") };
|
||||||
|
searEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder"));
|
||||||
|
searEdit->textInput().connect([this, searEdit]
|
||||||
|
{
|
||||||
|
refreshView(searEdit->text());
|
||||||
|
});
|
||||||
|
|
||||||
SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
|
SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
|
||||||
sortMode->sortModeChanged.connect([this](ReleaseCollector::Mode sortMode)
|
sortMode->sortModeChanged.connect([this](ReleaseCollector::Mode sortMode)
|
||||||
{
|
{
|
||||||
@@ -98,6 +106,12 @@ namespace lms::ui
|
|||||||
refreshView();
|
refreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Releases::refreshView(const Wt::WString& searchText)
|
||||||
|
{
|
||||||
|
_releaseCollector.setSearch(searchText.toUTF8());
|
||||||
|
refreshView();
|
||||||
|
}
|
||||||
|
|
||||||
void Releases::addSome()
|
void Releases::addSome()
|
||||||
{
|
{
|
||||||
const auto releaseIds{ _releaseCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}) };
|
const auto releaseIds{ _releaseCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}) };
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace lms::ui
|
|||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
void refreshView(ReleaseCollector::Mode mode);
|
void refreshView(ReleaseCollector::Mode mode);
|
||||||
|
void refreshView(const Wt::WString& searchText);
|
||||||
|
|
||||||
void addSome();
|
void addSome();
|
||||||
std::vector<db::ReleaseId> getAllReleases();
|
std::vector<db::ReleaseId> getAllReleases();
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 Emeric Poupon
|
|
||||||
*
|
|
||||||
* This file is part of LMS.
|
|
||||||
*
|
|
||||||
* LMS is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LMS is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "SearchView.hpp"
|
|
||||||
|
|
||||||
#include <Wt/WPushButton.h>
|
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/Session.hpp"
|
|
||||||
#include "database/Track.hpp"
|
|
||||||
|
|
||||||
#include "common/InfiniteScrollingContainer.hpp"
|
|
||||||
#include "ArtistListHelpers.hpp"
|
|
||||||
#include "Filters.hpp"
|
|
||||||
#include "LmsApplication.hpp"
|
|
||||||
#include "ReleaseHelpers.hpp"
|
|
||||||
#include "TrackArtistLinkTypeSelector.hpp"
|
|
||||||
#include "TrackListHelpers.hpp"
|
|
||||||
|
|
||||||
namespace lms::ui
|
|
||||||
{
|
|
||||||
using namespace db;
|
|
||||||
|
|
||||||
SearchView::SearchView(Filters& filters, PlayQueueController& playQueueController)
|
|
||||||
: Wt::WTemplate{ Wt::WString::tr("Lms.Explore.Search.template") }
|
|
||||||
, _playQueueController{ playQueueController }
|
|
||||||
, _filters{ filters }
|
|
||||||
, _artistCollector{ filters, ArtistCollector::Mode::Search, getMaxCount(Mode::Artist) }
|
|
||||||
, _releaseCollector{ filters, ReleaseCollector::Mode::Search, getMaxCount(Mode::Release) }
|
|
||||||
, _trackCollector{ filters, TrackCollector::Mode::Search, getMaxCount(Mode::Track) }
|
|
||||||
{
|
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
|
||||||
|
|
||||||
_stack = bindNew<Wt::WStackedWidget>("results");
|
|
||||||
_stack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
|
|
||||||
|
|
||||||
// releases
|
|
||||||
_releases = _stack->addNew<InfiniteScrollingContainer>(Wt::WString::tr("Lms.Explore.Releases.template.container"));
|
|
||||||
_releases->onRequestElements.connect([this] { addSomeReleases(); });
|
|
||||||
|
|
||||||
// artists
|
|
||||||
{
|
|
||||||
Wt::WTemplate* artistResults{ _stack->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Search.template.artists")) };
|
|
||||||
|
|
||||||
TrackArtistLinkTypeSelector* linkTypeSelector{ artistResults->bindNew<TrackArtistLinkTypeSelector>("link-type", std::nullopt)};
|
|
||||||
linkTypeSelector->linkTypeChanged.connect([this](std::optional<TrackArtistLinkType> linkType)
|
|
||||||
{
|
|
||||||
refreshView(linkType);
|
|
||||||
});
|
|
||||||
|
|
||||||
_artists = artistResults->bindNew<InfiniteScrollingContainer>("artists", Wt::WString::tr("Lms.Explore.Artists.template.container"));
|
|
||||||
_artists->onRequestElements.connect([this] { addSomeArtists(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracks
|
|
||||||
_tracks = _stack->addNew<InfiniteScrollingContainer>();
|
|
||||||
_tracks->onRequestElements.connect([this] { addSomeTracks(); });
|
|
||||||
|
|
||||||
// Menu
|
|
||||||
auto bindMenuItem{ [this](std::size_t index, const std::string& var, const Wt::WString& title)
|
|
||||||
{
|
|
||||||
Wt::WPushButton* menuItem {bindNew<Wt::WPushButton>(var, title)};
|
|
||||||
menuItem->clicked().connect([this, menuItem, index]
|
|
||||||
{
|
|
||||||
_stack->setCurrentIndex(index);
|
|
||||||
_currentActiveItem->removeStyleClass("active");
|
|
||||||
menuItem->addStyleClass("active");
|
|
||||||
_currentActiveItem = menuItem;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (index == 0)
|
|
||||||
{
|
|
||||||
_currentActiveItem = menuItem;
|
|
||||||
_currentActiveItem->addStyleClass("active");
|
|
||||||
}
|
|
||||||
} };
|
|
||||||
|
|
||||||
bindMenuItem(0, "releases", Wt::WString::tr("Lms.Explore.releases"));
|
|
||||||
bindMenuItem(1, "artists", Wt::WString::tr("Lms.Explore.artists"));
|
|
||||||
bindMenuItem(2, "tracks", Wt::WString::tr("Lms.Explore.tracks"));
|
|
||||||
|
|
||||||
filters.updated().connect([this]
|
|
||||||
{
|
|
||||||
refreshView();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t SearchView::getBatchSize(Mode mode) const
|
|
||||||
{
|
|
||||||
auto it{ _batchSizes.find(mode) };
|
|
||||||
assert(it != _batchSizes.cend());
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t SearchView::getMaxCount(Mode mode) const
|
|
||||||
{
|
|
||||||
auto it{ _maxCounts.find(mode) };
|
|
||||||
assert(it != _maxCounts.cend());
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::refreshView(std::optional<TrackArtistLinkType> linkType)
|
|
||||||
{
|
|
||||||
_artistCollector.setArtistLinkType(linkType);
|
|
||||||
refreshView();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::refreshView(const Wt::WString& searchText)
|
|
||||||
{
|
|
||||||
_releaseCollector.setSearch(searchText.toUTF8());
|
|
||||||
_artistCollector.setSearch(searchText.toUTF8());
|
|
||||||
_trackCollector.setSearch(searchText.toUTF8());
|
|
||||||
refreshView();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::refreshView()
|
|
||||||
{
|
|
||||||
_artists->reset();
|
|
||||||
_releases->reset();
|
|
||||||
_tracks->reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::addSomeArtists()
|
|
||||||
{
|
|
||||||
using namespace db;
|
|
||||||
|
|
||||||
const Range range{ _artists->getCount(), getBatchSize(Mode::Artist) };
|
|
||||||
const RangeResults<ArtistId> artistIds{ _artistCollector.get(range) };
|
|
||||||
{
|
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
|
||||||
|
|
||||||
for (const ArtistId artistId : artistIds.results)
|
|
||||||
{
|
|
||||||
const Artist::pointer artist{ Artist::find(LmsApp->getDbSession(), artistId) };
|
|
||||||
if (artist)
|
|
||||||
_artists->add(ArtistListHelpers::createEntry(artist));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_artists->setHasMore(artistIds.moreResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::addSomeReleases()
|
|
||||||
{
|
|
||||||
using namespace db;
|
|
||||||
|
|
||||||
const Range range{ _releases->getCount(), getBatchSize(Mode::Release) };
|
|
||||||
const RangeResults<ReleaseId> releaseIds{ _releaseCollector.get(range) };
|
|
||||||
{
|
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
|
||||||
|
|
||||||
for (const ReleaseId releaseId : releaseIds.results)
|
|
||||||
{
|
|
||||||
const Release::pointer release{ Release::find(LmsApp->getDbSession(), releaseId) };
|
|
||||||
if (release)
|
|
||||||
_releases->add(releaseListHelpers::createEntry(release));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_releases->setHasMore(releaseIds.moreResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::addSomeTracks()
|
|
||||||
{
|
|
||||||
using namespace db;
|
|
||||||
|
|
||||||
const Range range{ _tracks->getCount(), getBatchSize(Mode::Track) };
|
|
||||||
const RangeResults<TrackId> trackIds{ _trackCollector.get(range) };
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
|
||||||
|
|
||||||
for (const TrackId trackId : trackIds.results)
|
|
||||||
{
|
|
||||||
const Track::pointer track{ Track::find(LmsApp->getDbSession(), trackId) };
|
|
||||||
if (track)
|
|
||||||
_tracks->add(TrackListHelpers::createEntry(track, _playQueueController, _filters));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_tracks->setHasMore(trackIds.moreResults);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 Emeric Poupon
|
|
||||||
*
|
|
||||||
* This file is part of LMS.
|
|
||||||
*
|
|
||||||
* LMS is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* LMS is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <Wt/WComboBox.h>
|
|
||||||
#include <Wt/WStackedWidget.h>
|
|
||||||
#include <Wt/WTemplate.h>
|
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
|
||||||
#include "ArtistCollector.hpp"
|
|
||||||
#include "ReleaseCollector.hpp"
|
|
||||||
#include "TrackCollector.hpp"
|
|
||||||
|
|
||||||
namespace lms::ui
|
|
||||||
{
|
|
||||||
class InfiniteScrollingContainer;
|
|
||||||
class Filters;
|
|
||||||
class PlayQueueController;
|
|
||||||
|
|
||||||
class SearchView : public Wt::WTemplate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SearchView(Filters& filters, PlayQueueController& playQueueController);
|
|
||||||
|
|
||||||
void refreshView(const Wt::WString& searchText);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// same order as in the menu
|
|
||||||
enum class Mode
|
|
||||||
{
|
|
||||||
Artist,
|
|
||||||
Release,
|
|
||||||
Track,
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr Mode _defaultMode{ Mode::Release };
|
|
||||||
static inline std::unordered_map<Mode, std::size_t> _batchSizes
|
|
||||||
{
|
|
||||||
{Mode::Artist, 6},
|
|
||||||
{Mode::Release, 6},
|
|
||||||
{Mode::Track, 6},
|
|
||||||
};
|
|
||||||
static inline std::unordered_map<Mode, std::size_t> _maxCounts
|
|
||||||
{
|
|
||||||
{Mode::Artist, 1000},
|
|
||||||
{Mode::Release, 504},
|
|
||||||
{Mode::Track, 500},
|
|
||||||
};
|
|
||||||
std::size_t getBatchSize(Mode mode) const;
|
|
||||||
std::size_t getMaxCount(Mode mode) const;
|
|
||||||
|
|
||||||
void refreshView();
|
|
||||||
void refreshView(std::optional<db::TrackArtistLinkType> linkType);
|
|
||||||
void addSomeArtists();
|
|
||||||
void addSomeReleases();
|
|
||||||
void addSomeTracks();
|
|
||||||
|
|
||||||
PlayQueueController& _playQueueController;
|
|
||||||
Filters& _filters;
|
|
||||||
ArtistCollector _artistCollector;
|
|
||||||
ReleaseCollector _releaseCollector;
|
|
||||||
TrackCollector _trackCollector;
|
|
||||||
Wt::WStackedWidget* _stack{};
|
|
||||||
Wt::WWidget* _currentActiveItem {};
|
|
||||||
InfiniteScrollingContainer* _artists{};
|
|
||||||
InfiniteScrollingContainer* _releases{};
|
|
||||||
InfiniteScrollingContainer* _tracks{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace lms::ui
|
|
||||||
|
|
||||||
@@ -55,6 +55,7 @@ namespace lms::ui
|
|||||||
feedback::IFeedbackService::FindParameters params;
|
feedback::IFeedbackService::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
tracks = feedbackService.findStarredTracks(params);
|
tracks = feedbackService.findStarredTracks(params);
|
||||||
@@ -67,6 +68,7 @@ namespace lms::ui
|
|||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
tracks = scrobblingService.getRecentTracks(params);
|
tracks = scrobblingService.getRecentTracks(params);
|
||||||
@@ -79,6 +81,7 @@ namespace lms::ui
|
|||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
tracks = scrobblingService.getTopTracks(params);
|
tracks = scrobblingService.getTopTracks(params);
|
||||||
@@ -86,26 +89,12 @@ namespace lms::ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Mode::RecentlyAdded:
|
case Mode::RecentlyAdded:
|
||||||
{
|
|
||||||
Track::FindParameters params;
|
|
||||||
params.setClusters(getFilters().getClusters());
|
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
|
||||||
params.setSortMethod(TrackSortMethod::LastWritten);
|
|
||||||
params.setRange(range);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
|
||||||
tracks = Track::findIds(LmsApp->getDbSession(), params);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Mode::Search:
|
|
||||||
{
|
{
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
params.setKeywords(getSearchKeywords());
|
params.setKeywords(getSearchKeywords());
|
||||||
|
params.setSortMethod(TrackSortMethod::LastWritten);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -120,6 +109,7 @@ namespace lms::ui
|
|||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -145,6 +135,7 @@ namespace lms::ui
|
|||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(getFilters().getClusters());
|
params.setClusters(getFilters().getClusters());
|
||||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||||
|
params.setKeywords(getSearchKeywords());
|
||||||
params.setSortMethod(TrackSortMethod::Random);
|
params.setSortMethod(TrackSortMethod::Random);
|
||||||
params.setRange(Range{ 0, getMaxCount() });
|
params.setRange(Range{ 0, getMaxCount() });
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "TracksView.hpp"
|
#include "TracksView.hpp"
|
||||||
|
|
||||||
|
#include <Wt/WLineEdit.h>
|
||||||
#include <Wt/WPushButton.h>
|
#include <Wt/WPushButton.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
@@ -45,6 +46,13 @@ namespace lms::ui
|
|||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
|
|
||||||
|
Wt::WLineEdit* searEdit{ bindNew<Wt::WLineEdit>("search") };
|
||||||
|
searEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder"));
|
||||||
|
searEdit->textInput().connect([this, searEdit]
|
||||||
|
{
|
||||||
|
refreshView(searEdit->text());
|
||||||
|
});
|
||||||
|
|
||||||
SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
|
SortModeSelector* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
|
||||||
sortMode->sortModeChanged.connect([this](TrackCollector::Mode mode)
|
sortMode->sortModeChanged.connect([this](TrackCollector::Mode mode)
|
||||||
{
|
{
|
||||||
@@ -99,6 +107,12 @@ namespace lms::ui
|
|||||||
refreshView();
|
refreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Tracks::refreshView(const Wt::WString& searchText)
|
||||||
|
{
|
||||||
|
_trackCollector.setSearch(searchText.toUTF8());
|
||||||
|
refreshView();
|
||||||
|
}
|
||||||
|
|
||||||
void Tracks::addSome()
|
void Tracks::addSome()
|
||||||
{
|
{
|
||||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace lms::ui
|
|||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
void refreshView(TrackCollector::Mode mode);
|
void refreshView(TrackCollector::Mode mode);
|
||||||
|
void refreshView(const Wt::WString& searchText);
|
||||||
void addSome();
|
void addSome();
|
||||||
|
|
||||||
std::vector<db::TrackId> getAllTracks();
|
std::vector<db::TrackId> getAllTracks();
|
||||||
|
|||||||
Reference in New Issue
Block a user