Added basic UI to display shared playlists, ref #391

This commit is contained in:
emeric
2024-12-18 20:20:39 +01:00
parent d49d718922
commit 5c73b5bb97
13 changed files with 154 additions and 48 deletions
+7
View File
@@ -38,6 +38,8 @@ namespace lms::db
template<typename ResultType>
Wt::Dbo::Query<ResultType> createQuery(Session& session, std::string_view itemToSelect, const TrackList::FindParameters& params)
{
assert(!params.user.isValid() || !params.excludedUser.isValid());
auto query{ session.getDboSession()->query<ResultType>("SELECT " + std::string{ itemToSelect } + " FROM tracklist t_l") };
if (!params.clusters.empty() || params.mediaLibrary.isValid())
@@ -46,6 +48,9 @@ namespace lms::db
query.groupBy("t_l.id");
}
for (std::string_view keyword : params.keywords)
query.where("t_l.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
if (params.mediaLibrary.isValid())
query.join("track t ON t.id = t_l_e.track_id");
@@ -54,6 +59,8 @@ namespace lms::db
if (params.user.isValid())
query.where("t_l.user_id = ?").bind(params.user);
else if (params.excludedUser.isValid())
query.where("t_l.user_id <> ? OR t_l.user_id IS NULL").bind(params.excludedUser);
if (params.type)
query.where("t_l.type = ?").bind(*params.type);
@@ -62,10 +62,12 @@ namespace lms::db
// Search utility
struct FindParameters
{
std::vector<ClusterId> clusters; // if non empty, tracklists that have tracks that belong to these clusters
std::vector<ClusterId> clusters; // if non empty, tracklists that have tracks that belong to these clusters
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords (on either name field OR sort name field)
std::optional<Range> range;
std::optional<TrackListType> type;
UserId user; // only tracklists owned by this user
UserId excludedUser; // only tracklists *not* owned by this user
MediaLibraryId mediaLibrary; // only tracklists that have songs in this media library
TrackListSortMethod sortMethod{ TrackListSortMethod::None };
std::optional<Visibility> visibility;
@@ -75,6 +77,11 @@ namespace lms::db
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<Range> _range)
{
range = _range;
@@ -90,6 +97,12 @@ namespace lms::db
user = _user;
return *this;
}
FindParameters& setExcludedUser(UserId _user)
{
excludedUser = _user;
return *this;
}
FindParameters& setMediaLibrary(MediaLibraryId _mediaLibrary)
{
mediaLibrary = _mediaLibrary;
+4 -4
View File
@@ -45,10 +45,10 @@ 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());
Wt::WLineEdit* searchEdit{ bindNew<Wt::WLineEdit>("search") };
searchEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder"));
searchEdit->textInput().connect([this, searchEdit] {
refreshView(searchEdit->text());
});
{
+37 -26
View File
@@ -84,6 +84,13 @@ namespace lms::ui
if (!trackList)
throw TrackListNotFoundException{};
if (trackList->getUserId().isValid()
&& LmsApp->getUserId() != trackList->getUserId()
&& trackList->getVisibility() != db::TrackList::Visibility::Public)
{
throw TrackListNotFoundException{};
}
LmsApp->setTitle(std::string{ trackList->getName() });
_trackListId = *trackListId;
@@ -133,37 +140,41 @@ namespace lms::ui
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
->setLink(Wt::WLink{ std::make_unique<DownloadTrackListResource>(*trackListId) });
bindNew<Wt::WPushButton>("delete", Wt::WString::tr("Lms.delete"))
->clicked()
.connect([this, trackListId] {
auto modal{ std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.TrackList.template.delete-tracklist")) };
modal->addFunction("tr", &Wt::WTemplate::Functions::tr);
Wt::WWidget* modalPtr{ modal.get() };
if (trackList->getUserId() == LmsApp->getUserId())
{
setCondition("if-has-delete", true);
bindNew<Wt::WPushButton>("delete", Wt::WString::tr("Lms.delete"))
->clicked()
.connect([this, trackListId] {
auto modal{ std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.TrackList.template.delete-tracklist")) };
modal->addFunction("tr", &Wt::WTemplate::Functions::tr);
Wt::WWidget* modalPtr{ modal.get() };
auto* delBtn{ modal->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.delete")) };
delBtn->clicked().connect([this, trackListId, modalPtr] {
{
auto transaction{ LmsApp->getDbSession().createWriteTransaction() };
auto* delBtn{ modal->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.delete")) };
delBtn->clicked().connect([this, trackListId, modalPtr] {
{
auto transaction{ LmsApp->getDbSession().createWriteTransaction() };
db::TrackList::pointer trackList{ db::TrackList::find(LmsApp->getDbSession(), *trackListId) };
if (trackList)
trackList.remove();
}
db::TrackList::pointer trackList{ db::TrackList::find(LmsApp->getDbSession(), *trackListId) };
if (trackList)
trackList.remove();
}
clear();
trackListDeleted.emit(*trackListId);
LmsApp->setInternalPath("/tracklists", true);
clear();
trackListDeleted.emit(*trackListId);
LmsApp->setInternalPath("/tracklists", true);
LmsApp->getModalManager().dispose(modalPtr);
LmsApp->getModalManager().dispose(modalPtr);
});
auto* cancelBtn{ modal->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel")) };
cancelBtn->clicked().connect([=] {
LmsApp->getModalManager().dispose(modalPtr);
});
LmsApp->getModalManager().show(std::move(modal));
});
auto* cancelBtn{ modal->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel")) };
cancelBtn->clicked().connect([=] {
LmsApp->getModalManager().dispose(modalPtr);
});
LmsApp->getModalManager().show(std::move(modal));
});
}
_container = bindNew<InfiniteScrollingContainer>("tracks", Wt::WString::tr("Lms.Explore.TrackList.template.entry-container"));
_container->onRequestElements.connect([this] {
+50 -12
View File
@@ -19,6 +19,7 @@
#include "TrackListsView.hpp"
#include <Wt/WLineEdit.h>
#include <Wt/WPushButton.h>
#include "database/Session.hpp"
@@ -44,16 +45,38 @@ namespace lms::ui
addFunction("id", &Wt::WTemplate::Functions::id);
{
_mode = state::readValue<Mode>("tracklists_sort_mode").value_or(_defaultMode);
_type = state::readValue<Type>("tracklists_type").value_or(_defaultType);
using SortModeSelector = DropDownMenuSelector<TrackLists::Mode>;
SortModeSelector* sortModeSelector{ bindNew<SortModeSelector>("sort-mode", Wt::WString::tr("Lms.Explore.TrackLists.template.sort-mode"), _mode) };
sortModeSelector->bindItem("recently-modified", Wt::WString::tr("Lms.Explore.recently-modified"), Mode::RecentlyModified);
sortModeSelector->bindItem("all", Wt::WString::tr("Lms.Explore.all"), Mode::All);
using TypeSelector = DropDownMenuSelector<Type>;
TypeSelector* typeSelector{ bindNew<TypeSelector>("tracklist-type", Wt::WString::tr("Lms.Explore.TrackLists.template.type-selector"), _type) };
typeSelector->bindItem("owned", Wt::WString::tr("Lms.Explore.TrackLists.type-owned"), Type::Owned);
typeSelector->bindItem("shared", Wt::WString::tr("Lms.Explore.TrackLists.type-shared"), Type::Shared);
sortModeSelector->itemSelected.connect(this, [this](TrackLists::Mode mode) {
state::writeValue<Mode>("tracklists_sort_mode", mode);
_mode = mode;
typeSelector->itemSelected.connect(this, [this](Type type) {
state::writeValue<Type>("tracklists_type", type);
_type = type;
refreshView();
});
}
Wt::WLineEdit* searchEdit{ bindNew<Wt::WLineEdit>("search") };
searchEdit->setPlaceholderText(Wt::WString::tr("Lms.Explore.Search.search-placeholder"));
searchEdit->textInput().connect([this, searchEdit] {
_searchText = searchEdit->text().toUTF8();
refreshView();
});
{
_sortMode = state::readValue<SortMode>("tracklists_sort_mode").value_or(_defaultSortMode);
using SortModeSelector = DropDownMenuSelector<SortMode>;
SortModeSelector* sortModeSelector{ bindNew<SortModeSelector>("sort-mode", Wt::WString::tr("Lms.Explore.TrackLists.template.sort-mode"), _sortMode) };
sortModeSelector->bindItem("recently-modified", Wt::WString::tr("Lms.Explore.recently-modified"), SortMode::RecentlyModified);
sortModeSelector->bindItem("all", Wt::WString::tr("Lms.Explore.all"), SortMode::All);
sortModeSelector->itemSelected.connect(this, [this](SortMode mode) {
state::writeValue<SortMode>("tracklists_sort_mode", mode);
_sortMode = mode;
refreshView();
});
}
@@ -94,17 +117,32 @@ namespace lms::ui
auto transaction{ session.createReadTransaction() };
TrackList::FindParameters params;
if (!_searchText.empty())
params.setKeywords(core::stringUtils::splitString(_searchText, ' '));
params.setClusters(_filters.getClusters());
params.setMediaLibrary(_filters.getMediaLibrary());
params.setUser(LmsApp->getUserId());
params.setType(TrackListType::PlayList);
params.setRange(range);
switch (_mode)
switch (_type)
{
case Mode::All:
case Type::Owned:
params.setUser(LmsApp->getUserId());
break;
case Type::Shared:
params.setExcludedUser(LmsApp->getUserId());
params.setVisibility(db::TrackList::Visibility::Public);
break;
}
switch (_sortMode)
{
case SortMode::All:
params.setSortMethod(TrackListSortMethod::Name);
break;
case Mode::RecentlyModified:
case SortMode::RecentlyModified:
params.setSortMethod(TrackListSortMethod::LastModifiedDesc);
break;
}
+12 -3
View File
@@ -46,21 +46,30 @@ namespace lms::ui
void onTrackListDeleted(db::TrackListId trackListId);
private:
enum class Mode
enum class SortMode
{
RecentlyModified,
All,
};
enum class Type
{
Owned,
Shared,
};
void refreshView();
void addSome();
void addTracklist(const db::ObjectPtr<db::TrackList>& trackList);
static constexpr Mode _defaultMode{ Mode::RecentlyModified };
static constexpr SortMode _defaultSortMode{ SortMode::RecentlyModified };
static constexpr Type _defaultType{ Type::Owned };
static constexpr std::size_t _batchSize{ 30 };
static constexpr std::size_t _maxCount{ 500 };
Mode _mode;
SortMode _sortMode{ _defaultSortMode };
Type _type{ _defaultType };
std::string _searchText;
Filters& _filters;
Wt::WWidget* _currentActiveItem{};
InfiniteScrollingContainer* _container{};