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:
+4
-1
@@ -4,9 +4,12 @@
|
||||
|
||||
<message id="Lms.Explore.Artists.template">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
${link-type}
|
||||
${link-type class="me-1"}
|
||||
<div class="d-flex">
|
||||
${search class="form-control me-1" type="search"}
|
||||
${sort-mode}
|
||||
</div>
|
||||
</div>
|
||||
${artists}
|
||||
</message>
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
<message id="Lms.main.template.navbar">
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-3">
|
||||
<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">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<message id="Lms.Explore.Releases.template">
|
||||
<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"}
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||
<ul class="dropdown-menu">
|
||||
@@ -13,8 +13,11 @@
|
||||
<li>${play-shuffled class="dropdown-item"}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
${search class="form-control me-1" type="search"}
|
||||
${sort-mode}
|
||||
</div>
|
||||
</div>
|
||||
${releases}
|
||||
</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>
|
||||
+4
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
<message id="Lms.Explore.Tracks.template">
|
||||
<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"}
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||
<ul class="dropdown-menu">
|
||||
@@ -14,8 +14,11 @@
|
||||
<li>${play-shuffled class="dropdown-item"}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
${search class="form-control me-1" type="search"}
|
||||
${sort-mode}
|
||||
</div>
|
||||
</div>
|
||||
${tracks}
|
||||
</message>
|
||||
|
||||
|
||||
@@ -32,9 +32,8 @@ namespace lms::db
|
||||
Wt::Dbo::Query<ArtistId> createArtistsQuery(Session& session, const Listen::ArtistStatsFindParameters& params)
|
||||
{
|
||||
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("listen l ON l.track_id = t.id") };
|
||||
.join("listen l ON l.track_id = t_a_l.track_id") };
|
||||
|
||||
if (params.user.isValid())
|
||||
query.where("l.user_id = ?").bind(params.user);
|
||||
@@ -45,7 +44,10 @@ namespace lms::db
|
||||
assert(!params.artist.isValid()); // poor check
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (params.linkType)
|
||||
query.where("t_a_l.type = ?").bind(*params.linkType);
|
||||
@@ -69,6 +71,26 @@ namespace lms::db
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -114,6 +136,9 @@ namespace lms::db
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -157,6 +182,9 @@ namespace lms::db
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace lms::db
|
||||
UserId user;
|
||||
std::optional<ScrobblingBackend> backend;
|
||||
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;
|
||||
ArtistId artist; // if set, matching this artist
|
||||
MediaLibraryId library;
|
||||
@@ -77,6 +78,7 @@ namespace lms::db
|
||||
StatsFindParameters& setUser(UserId _user) { user = _user; return *this; }
|
||||
StatsFindParameters& setScrobblingBackend(std::optional<ScrobblingBackend> _backend) { backend = _backend; 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& setArtist(ArtistId _artist) { artist = _artist; return *this; }
|
||||
StatsFindParameters& setMediaLibrary(MediaLibraryId _library) { library = _library; return *this; }
|
||||
|
||||
@@ -96,6 +96,7 @@ namespace lms::feedback
|
||||
Artist::FindParameters searchParams;
|
||||
searchParams.setStarringUser(params.user, *backend);
|
||||
searchParams.setClusters(params.clusters);
|
||||
searchParams.setKeywords(params.keywords);
|
||||
searchParams.setLinkType(params.linkType);
|
||||
searchParams.setSortMethod(params.sortMethod);
|
||||
searchParams.setRange(params.range);
|
||||
@@ -136,6 +137,7 @@ namespace lms::feedback
|
||||
Release::FindParameters searchParams;
|
||||
searchParams.setStarringUser(params.user, *backend);
|
||||
searchParams.setClusters(params.clusters);
|
||||
searchParams.setKeywords(params.keywords);
|
||||
searchParams.setSortMethod(ReleaseSortMethod::StarredDateDesc);
|
||||
searchParams.setRange(params.range);
|
||||
searchParams.setMediaLibrary(params.library);
|
||||
@@ -175,6 +177,7 @@ namespace lms::feedback
|
||||
Track::FindParameters searchParams;
|
||||
searchParams.setStarringUser(params.user, *backend);
|
||||
searchParams.setClusters(params.clusters);
|
||||
searchParams.setKeywords(params.keywords);
|
||||
searchParams.setSortMethod(TrackSortMethod::StarredDateDesc);
|
||||
searchParams.setRange(params.range);
|
||||
searchParams.setMediaLibrary(params.library);
|
||||
|
||||
@@ -54,11 +54,13 @@ namespace lms::feedback
|
||||
{
|
||||
db::UserId user;
|
||||
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;
|
||||
db::MediaLibraryId library;
|
||||
|
||||
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& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||
FindParameters& setRange(std::optional<db::Range> _range) { range = _range; return *this; }
|
||||
FindParameters& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace lms::scrobbling
|
||||
db::Listen::StatsFindParameters listenFindParams;
|
||||
listenFindParams.setUser(params.user);
|
||||
listenFindParams.setClusters(params.clusters);
|
||||
listenFindParams.setKeywords(params.keywords);
|
||||
listenFindParams.setRange(params.range);
|
||||
listenFindParams.setMediaLibrary(params.library);
|
||||
listenFindParams.setArtist(params.artist);
|
||||
|
||||
@@ -61,12 +61,14 @@ namespace lms::scrobbling
|
||||
{
|
||||
db::UserId user;
|
||||
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;
|
||||
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& 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& setMediaLibrary(db::MediaLibraryId _library) { library = _library; return *this; }
|
||||
FindParameters& setArtist(db::ArtistId _artist) { artist = _artist; return *this; }
|
||||
|
||||
@@ -42,7 +42,6 @@ add_executable(lms
|
||||
ui/explore/ReleasesView.cpp
|
||||
ui/explore/ReleaseView.cpp
|
||||
ui/explore/ReleaseTypes.cpp
|
||||
ui/explore/SearchView.cpp
|
||||
ui/explore/SortModeSelector.cpp
|
||||
ui/explore/TrackArtistLinkTypeSelector.cpp
|
||||
ui/explore/TrackCollector.cpp
|
||||
|
||||
@@ -97,7 +97,6 @@ namespace lms::ui
|
||||
res->use(appRoot + "playqueue");
|
||||
res->use(appRoot + "release");
|
||||
res->use(appRoot + "releases");
|
||||
res->use(appRoot + "search");
|
||||
res->use(appRoot + "settings");
|
||||
res->use(appRoot + "tracklist");
|
||||
res->use(appRoot + "tracklists");
|
||||
@@ -139,7 +138,6 @@ namespace lms::ui
|
||||
{ "/artist", IdxExplore, false, std::nullopt },
|
||||
{ "/releases", IdxExplore, false, Wt::WString::tr("Lms.Explore.releases") },
|
||||
{ "/release", IdxExplore, false, std::nullopt },
|
||||
{ "/search", IdxExplore, false, Wt::WString::tr("Lms.Explore.search") },
|
||||
{ "/tracks", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracks") },
|
||||
{ "/tracklists", IdxExplore, false, Wt::WString::tr("Lms.Explore.tracklists") },
|
||||
{ "/tracklist", IdxExplore, false, std::nullopt },
|
||||
@@ -435,9 +433,6 @@ namespace lms::ui
|
||||
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)
|
||||
{
|
||||
navbar->setCondition("if-is-admin", true);
|
||||
@@ -462,17 +457,6 @@ namespace lms::ui
|
||||
_playQueue = mainStack->addWidget(std::move(playQueue));
|
||||
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
|
||||
if (getUserType() == db::UserType::ADMIN)
|
||||
{
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace lms::ui
|
||||
feedback::IFeedbackService::ArtistFindParameters params;
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(filters.getClusters());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setMediaLibrary(filters.getMediaLibrary());
|
||||
params.setLinkType(_linkType);
|
||||
params.setSortMethod(ArtistSortMethod::StarredDateDesc);
|
||||
@@ -67,6 +68,7 @@ namespace lms::ui
|
||||
scrobbling::IScrobblingService::ArtistFindParameters params;
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(filters.getClusters());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setMediaLibrary(filters.getMediaLibrary());
|
||||
params.setLinkType(_linkType);
|
||||
params.setRange(range);
|
||||
@@ -80,6 +82,7 @@ namespace lms::ui
|
||||
scrobbling::IScrobblingService::ArtistFindParameters params;
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(filters.getClusters());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setMediaLibrary(filters.getMediaLibrary());
|
||||
params.setLinkType(_linkType);
|
||||
params.setRange(range);
|
||||
@@ -92,6 +95,7 @@ namespace lms::ui
|
||||
{
|
||||
Artist::FindParameters params;
|
||||
params.setClusters(filters.getClusters());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setMediaLibrary(filters.getMediaLibrary());
|
||||
params.setLinkType(_linkType);
|
||||
params.setSortMethod(ArtistSortMethod::LastWritten);
|
||||
@@ -104,27 +108,12 @@ namespace lms::ui
|
||||
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:
|
||||
{
|
||||
Artist::FindParameters params;
|
||||
params.setClusters(filters.getClusters());
|
||||
params.setMediaLibrary(filters.getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setLinkType(_linkType);
|
||||
params.setSortMethod(ArtistSortMethod::SortName);
|
||||
params.setRange(range);
|
||||
@@ -152,6 +141,7 @@ namespace lms::ui
|
||||
Artist::FindParameters params;
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setLinkType(_linkType);
|
||||
params.setSortMethod(ArtistSortMethod::Random);
|
||||
params.setRange(Range{ 0, getMaxCount() });
|
||||
|
||||
@@ -38,10 +38,18 @@ namespace lms::ui
|
||||
using namespace db;
|
||||
|
||||
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 }
|
||||
{
|
||||
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->sortModeChanged.connect([this](ArtistCollector::Mode sortMode)
|
||||
@@ -87,6 +95,12 @@ namespace lms::ui
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void Artists::refreshView(const Wt::WString& searchText)
|
||||
{
|
||||
_artistCollector.setSearch(searchText.toUTF8());
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void Artists::addSome()
|
||||
{
|
||||
const auto artistIds{ _artistCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}) };
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <Wt/WTemplate.h>
|
||||
|
||||
#include "database/Types.hpp"
|
||||
#include "common/Template.hpp"
|
||||
#include "ArtistCollector.hpp"
|
||||
|
||||
namespace lms::ui
|
||||
@@ -33,7 +34,7 @@ namespace lms::ui
|
||||
class Filters;
|
||||
class InfiniteScrollingContainer;
|
||||
|
||||
class Artists : public Wt::WTemplate
|
||||
class Artists : public Template
|
||||
{
|
||||
public:
|
||||
Artists(Filters& filters);
|
||||
@@ -42,6 +43,7 @@ namespace lms::ui
|
||||
void refreshView();
|
||||
void refreshView(ArtistCollector::Mode mode);
|
||||
void refreshView(std::optional<db::TrackArtistLinkType> linkType);
|
||||
void refreshView(const Wt::WString& searchText);
|
||||
void addSome();
|
||||
|
||||
static constexpr std::size_t _batchSize{ 30 };
|
||||
|
||||
@@ -59,7 +59,10 @@ namespace lms::ui
|
||||
void DatabaseCollectorBase::setSearch(std::string_view searchText)
|
||||
{
|
||||
_searchText = searchText;
|
||||
if (!searchText.empty())
|
||||
_searchKeywords = core::stringUtils::splitString(_searchText, ' ');
|
||||
else
|
||||
_searchKeywords.clear();
|
||||
}
|
||||
|
||||
} // ns UserInterface
|
||||
|
||||
@@ -45,7 +45,6 @@ namespace lms::ui
|
||||
RecentlyPlayed,
|
||||
RecentlyAdded,
|
||||
MostPlayed,
|
||||
Search,
|
||||
All
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "Filters.hpp"
|
||||
#include "ReleasesView.hpp"
|
||||
#include "ReleaseView.hpp"
|
||||
#include "SearchView.hpp"
|
||||
#include "TrackListView.hpp"
|
||||
#include "TrackListsView.hpp"
|
||||
#include "TracksView.hpp"
|
||||
@@ -46,7 +45,6 @@ namespace lms::ui
|
||||
IdxTrackList,
|
||||
IdxReleases,
|
||||
IdxRelease,
|
||||
IdxSearch,
|
||||
IdxTracks,
|
||||
};
|
||||
|
||||
@@ -58,7 +56,6 @@ namespace lms::ui
|
||||
{ "/tracklist", IdxTrackList },
|
||||
{ "/releases", IdxReleases },
|
||||
{ "/release", IdxRelease },
|
||||
{ "/search", IdxSearch },
|
||||
{ "/tracks", IdxTracks },
|
||||
};
|
||||
|
||||
@@ -103,10 +100,6 @@ namespace lms::ui
|
||||
auto release = std::make_unique<Release>(filters, _playQueueController);
|
||||
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);
|
||||
contentsStack->addWidget(std::move(tracks));
|
||||
|
||||
@@ -117,9 +110,4 @@ namespace lms::ui
|
||||
|
||||
handleContentsPathChange(contentsStack);
|
||||
}
|
||||
|
||||
void Explore::search(const Wt::WString& searchText)
|
||||
{
|
||||
_search->refreshView(searchText);
|
||||
}
|
||||
} // namespace lms::ui
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace lms::ui
|
||||
public:
|
||||
Explore(Filters& filters, PlayQueue& playQueue);
|
||||
|
||||
void search(const Wt::WString& searchText);
|
||||
PlayQueueController& getPlayQueueController() { return _playQueueController; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace lms::ui
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
releases = feedbackService.findStarredReleases(params);
|
||||
break;
|
||||
@@ -64,6 +65,7 @@ namespace lms::ui
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
|
||||
releases = scrobblingService.getRecentReleases(params);
|
||||
@@ -76,6 +78,7 @@ namespace lms::ui
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
|
||||
releases = scrobblingService.getTopReleases(params);
|
||||
@@ -83,26 +86,12 @@ namespace lms::ui
|
||||
}
|
||||
|
||||
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;
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setSortMethod(ReleaseSortMethod::LastWritten);
|
||||
params.setRange(range);
|
||||
|
||||
{
|
||||
@@ -118,6 +107,7 @@ namespace lms::ui
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setSortMethod(ReleaseSortMethod::Name);
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
|
||||
{
|
||||
@@ -143,6 +133,7 @@ namespace lms::ui
|
||||
Release::FindParameters params;
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setSortMethod(ReleaseSortMethod::Random);
|
||||
params.setRange(Range{ 0, getMaxCount() });
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "ReleasesView.hpp"
|
||||
|
||||
#include <Wt/WLineEdit.h>
|
||||
#include <Wt/WPushButton.h>
|
||||
|
||||
#include "database/Release.hpp"
|
||||
@@ -44,6 +45,13 @@ namespace lms::ui
|
||||
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* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
|
||||
sortMode->sortModeChanged.connect([this](ReleaseCollector::Mode sortMode)
|
||||
{
|
||||
@@ -98,6 +106,12 @@ namespace lms::ui
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void Releases::refreshView(const Wt::WString& searchText)
|
||||
{
|
||||
_releaseCollector.setSearch(searchText.toUTF8());
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void Releases::addSome()
|
||||
{
|
||||
const auto releaseIds{ _releaseCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}) };
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace lms::ui
|
||||
private:
|
||||
void refreshView();
|
||||
void refreshView(ReleaseCollector::Mode mode);
|
||||
void refreshView(const Wt::WString& searchText);
|
||||
|
||||
void addSome();
|
||||
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;
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
params.setUser(LmsApp->getUserId());
|
||||
tracks = feedbackService.findStarredTracks(params);
|
||||
@@ -67,6 +68,7 @@ namespace lms::ui
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
|
||||
tracks = scrobblingService.getRecentTracks(params);
|
||||
@@ -79,6 +81,7 @@ namespace lms::ui
|
||||
params.setUser(LmsApp->getUserId());
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
|
||||
tracks = scrobblingService.getTopTracks(params);
|
||||
@@ -86,26 +89,12 @@ namespace lms::ui
|
||||
}
|
||||
|
||||
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;
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setSortMethod(TrackSortMethod::LastWritten);
|
||||
params.setRange(range);
|
||||
|
||||
{
|
||||
@@ -120,6 +109,7 @@ namespace lms::ui
|
||||
Track::FindParameters params;
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setRange(range);
|
||||
|
||||
{
|
||||
@@ -145,6 +135,7 @@ namespace lms::ui
|
||||
Track::FindParameters params;
|
||||
params.setClusters(getFilters().getClusters());
|
||||
params.setMediaLibrary(getFilters().getMediaLibrary());
|
||||
params.setKeywords(getSearchKeywords());
|
||||
params.setSortMethod(TrackSortMethod::Random);
|
||||
params.setRange(Range{ 0, getMaxCount() });
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "TracksView.hpp"
|
||||
|
||||
#include <Wt/WLineEdit.h>
|
||||
#include <Wt/WPushButton.h>
|
||||
|
||||
#include "database/Session.hpp"
|
||||
@@ -45,6 +46,13 @@ namespace lms::ui
|
||||
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* sortMode{ bindNew<SortModeSelector>("sort-mode", _defaultMode) };
|
||||
sortMode->sortModeChanged.connect([this](TrackCollector::Mode mode)
|
||||
{
|
||||
@@ -99,6 +107,12 @@ namespace lms::ui
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void Tracks::refreshView(const Wt::WString& searchText)
|
||||
{
|
||||
_trackCollector.setSearch(searchText.toUTF8());
|
||||
refreshView();
|
||||
}
|
||||
|
||||
void Tracks::addSome()
|
||||
{
|
||||
auto transaction{ LmsApp->getDbSession().createReadTransaction() };
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace lms::ui
|
||||
private:
|
||||
void refreshView();
|
||||
void refreshView(TrackCollector::Mode mode);
|
||||
void refreshView(const Wt::WString& searchText);
|
||||
void addSome();
|
||||
|
||||
std::vector<db::TrackId> getAllTracks();
|
||||
|
||||
Reference in New Issue
Block a user