Improved work display, when available, ref #834

This commit is contained in:
emeric
2026-07-16 18:54:48 +02:00
parent cb6863fe1e
commit 735c170a44
7 changed files with 56 additions and 32 deletions
+8 -7
View File
@@ -40,18 +40,19 @@
<div class="p-1"> <div class="p-1">
${cover} ${cover}
</div> </div>
<div class="row align-items-center flex-fill overflow-hidden"> <div class="row align-items-center flex-fill">
<div class="col-12 col-md-6 col-lg-4"> <div class="col-12 col-md-6 col-lg-4">
<div class="p-2 overflow-hidden"> <div class="p-2">
<div class="d-block text-truncate">${name}</div> <div class="d-block">${name}</div>
${<if-has-artists>}${artists class="d-block d-md-none text-truncate"}${</if-has-artists>} ${<if-has-artists>}${artists class="d-block d-md-none"}${</if-has-artists>}
${<if-has-work>}<div class="text-muted small ms-2">${work}</div>${</if-has-work>}
</div> </div>
</div> </div>
<div class="col-md-6 col-lg-4"> <div class="col-md-6 col-lg-4">
${<if-has-artists>}${artists-md class="d-none d-md-block text-truncate"}${</if-has-artists>} ${<if-has-artists>}${artists-md class="d-none d-md-block"}${</if-has-artists>}
</div> </div>
<div class="col-lg-4 text-truncate"> <div class="col-lg-4">
${<if-has-release>}${release class="d-none d-lg-inline text-truncate text-decoration-none link-success"}${</if-has-release>} ${<if-has-release>}${release class="d-none d-lg-inline text-decoration-none link-success"}${</if-has-release>}
</div> </div>
</div> </div>
<div class="p-2 d-none d-sm-block text-muted text-center Lms-tracks-entry-duration"> <div class="p-2 d-none d-sm-block text-muted text-center Lms-tracks-entry-duration">
+8 -7
View File
@@ -32,18 +32,19 @@
<div class="p-1"> <div class="p-1">
${cover} ${cover}
</div> </div>
<div class="row align-items-center flex-fill overflow-hidden"> <div class="row align-items-center flex-fill">
<div class="col-12 col-md-6 col-lg-4"> <div class="col-12 col-md-6 col-lg-4">
<div class="p-2 overflow-hidden"> <div class="p-2">
<div class="d-block text-truncate">${name}</div> <div class="d-block">${name}</div>
${<if-has-artists>}${artists class="d-block d-md-none text-truncate"}${</if-has-artists>} ${<if-has-artists>}${artists class="d-block d-md-none"}${</if-has-artists>}
${<if-has-work>}<div class="text-muted small ms-2">${work}</div>${</if-has-work>}
</div> </div>
</div> </div>
<div class="col-md-6 col-lg-4"> <div class="col-md-6 col-lg-4">
${<if-has-artists>}${artists-md class="d-none d-md-block text-truncate"}${</if-has-artists>} ${<if-has-artists>}${artists-md class="d-none d-md-block"}${</if-has-artists>}
</div> </div>
<div class="col-lg-4 text-truncate"> <div class="col-lg-4">
${<if-has-release>}${release class="d-none d-lg-inline text-truncate text-decoration-none link-success"}${</if-has-release>} ${<if-has-release>}${release class="d-none d-lg-inline text-decoration-none link-success"}${</if-has-release>}
</div> </div>
</div> </div>
<div class="p-2 d-none d-sm-block text-muted text-center Lms-tracks-entry-duration"> <div class="p-2 d-none d-sm-block text-muted text-center Lms-tracks-entry-duration">
+1 -1
View File
@@ -244,7 +244,7 @@ namespace lms::ui
const std::string nativeResource{ _audioFileResource->getUrl(trackId) }; const std::string nativeResource{ _audioFileResource->getUrl(trackId) };
const auto artistDisplayInfo{ utils::computeArtistDisplayInfo(track, db::TrackArtistLinkType::Artist) }; const auto artistDisplayInfo{ utils::computeArtistDisplayInfo(track, db::TrackArtistLinkType::Artist) };
const std::string displayTitle{ utils::computeTrackDisplayTitle(track) }; const std::string displayTitle{ utils::computeTrackDisplayInfo(track).title };
oss oss
<< "var params = {" << "var params = {"
+9 -4
View File
@@ -497,12 +497,17 @@ namespace lms::ui
const auto track{ tracklistEntry->getTrack() }; const auto track{ tracklistEntry->getTrack() };
const db::TrackId trackId{ track->getId() }; const db::TrackId trackId{ track->getId() };
const std::string displayTitle{ utils::computeTrackDisplayTitle(track) }; const utils::TrackDisplayInfo displayInfo{ utils::computeTrackDisplayInfo(track) };
Template* entry{ _entriesContainer->addNew<Template>(Wt::WString::tr("Lms.PlayQueue.template.entry")) }; Template* entry{ _entriesContainer->addNew<Template>(Wt::WString::tr("Lms.PlayQueue.template.entry")) };
entry->addFunction("id", &Wt::WTemplate::Functions::id); entry->addFunction("id", &Wt::WTemplate::Functions::id);
entry->bindString("name", Wt::WString::fromUTF8(displayTitle), Wt::TextFormat::Plain); entry->bindString("name", Wt::WString::fromUTF8(displayInfo.title), Wt::TextFormat::Plain);
if (displayInfo.workName)
{
entry->setCondition("if-has-work", true);
entry->bindString("work", Wt::WString::fromUTF8(*displayInfo.workName), Wt::TextFormat::Plain);
}
const auto artists{ track->getArtistIds({ db::TrackArtistLinkType::Artist }) }; const auto artists{ track->getArtistIds({ db::TrackArtistLinkType::Artist }) };
if (!artists.empty()) if (!artists.empty())
@@ -541,7 +546,7 @@ namespace lms::ui
entry->bindString("duration", utils::durationToString(track->getDuration()), Wt::TextFormat::Plain); entry->bindString("duration", utils::durationToString(track->getDuration()), Wt::TextFormat::Plain);
Wt::WPushButton* playBtn{ entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) }; Wt::WPushButton* playBtn{ entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) };
playBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.play-item").arg(displayTitle)); playBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.play-item").arg(displayInfo.title));
playBtn->clicked().connect([this, entry] { playBtn->clicked().connect([this, entry] {
const std::optional<std::size_t> pos{ _entriesContainer->getIndexOf(*entry) }; const std::optional<std::size_t> pos{ _entriesContainer->getIndexOf(*entry) };
if (pos) if (pos)
@@ -549,7 +554,7 @@ namespace lms::ui
}); });
Wt::WPushButton* delBtn{ entry->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.template.delete-btn"), Wt::TextFormat::XHTML) }; Wt::WPushButton* delBtn{ entry->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.template.delete-btn"), Wt::TextFormat::XHTML) };
delBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.delete-item").arg(displayTitle)); delBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.delete-item").arg(displayInfo.title));
delBtn->setToolTip(Wt::WString::tr("Lms.delete")); delBtn->setToolTip(Wt::WString::tr("Lms.delete"));
delBtn->clicked().connect([this, tracklistEntryId, entry] { delBtn->clicked().connect([this, tracklistEntryId, entry] {
// Remove the entry n both the widget tree and the playqueue // Remove the entry n both the widget tree and the playqueue
+14 -7
View File
@@ -43,6 +43,7 @@
#include "database/objects/Track.hpp" #include "database/objects/Track.hpp"
#include "database/objects/TrackArtistLink.hpp" #include "database/objects/TrackArtistLink.hpp"
#include "database/objects/TrackList.hpp" #include "database/objects/TrackList.hpp"
#include "database/objects/Work.hpp"
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
#include "ModalManager.hpp" #include "ModalManager.hpp"
@@ -562,25 +563,31 @@ namespace lms::ui::utils
LmsApp->doJavaScript("navigator.clipboard.writeText('" + core::stringUtils::jsEscape(text) + "').catch(function(){})"); LmsApp->doJavaScript("navigator.clipboard.writeText('" + core::stringUtils::jsEscape(text) + "').catch(function(){})");
} }
std::string computeTrackDisplayTitle(const db::ObjectPtr<db::Track>& track) TrackDisplayInfo computeTrackDisplayInfo(const db::ObjectPtr<db::Track>& track)
{ {
const auto movements{ track->getMovements() }; const auto movements{ track->getMovements() };
if (!movements.empty() && track->hasWork()) if (!movements.empty() && track->hasWork())
{ {
const db::Movement::pointer& movement{ movements.front() }; const db::Movement::pointer& movement{ movements.front() };
std::string result; std::string title;
if (const auto n{ movement->getNumber() }) if (const auto n{ movement->getNumber() })
{ {
if (const std::string numeral{ core::stringUtils::toRomanNumeral(*n) }; !numeral.empty()) if (const std::string numeral{ core::stringUtils::toRomanNumeral(*n) }; !numeral.empty())
result += numeral + ". "; title += numeral + ". ";
} }
result += movement->getName(); title += movement->getName();
if (!result.empty()) if (!title.empty())
return result; {
TrackDisplayInfo info{ .title = std::move(title), .workName = std::nullopt };
if (const auto works{ track->getWorks() }; !works.empty())
info.workName = std::string{ works.front()->getName() };
return info;
}
} }
return std::string{ track->getName() }; return TrackDisplayInfo{ .title = std::string{ track->getName() }, .workName = std::nullopt };
} }
} // namespace lms::ui::utils } // namespace lms::ui::utils
+7 -2
View File
@@ -21,6 +21,7 @@
#include <chrono> #include <chrono>
#include <map> #include <map>
#include <optional>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
@@ -104,6 +105,10 @@ namespace lms::ui::utils
void copyToClipboard(std::string_view text); void copyToClipboard(std::string_view text);
// Returns "i. MovementName" when track has work(s) + movement(s), otherwise track->getName(). struct TrackDisplayInfo
std::string computeTrackDisplayTitle(const db::ObjectPtr<db::Track>& track); {
std::string title; // "i. MovementName" when track has work + movement, otherwise just the track's title
std::optional<std::string> workName; // only set when title comes from a movement
};
TrackDisplayInfo computeTrackDisplayInfo(const db::ObjectPtr<db::Track>& track);
} // namespace lms::ui::utils } // namespace lms::ui::utils
+9 -4
View File
@@ -230,8 +230,13 @@ namespace lms::ui::TrackListHelpers
auto entry{ std::make_unique<Template>(Wt::WString::tr("Lms.Explore.Tracks.template.entry")) }; auto entry{ std::make_unique<Template>(Wt::WString::tr("Lms.Explore.Tracks.template.entry")) };
auto* entryPtr{ entry.get() }; auto* entryPtr{ entry.get() };
const std::string displayTitle{ utils::computeTrackDisplayTitle(track) }; const utils::TrackDisplayInfo displayInfo{ utils::computeTrackDisplayInfo(track) };
entry->bindString("name", Wt::WString::fromUTF8(displayTitle), Wt::TextFormat::Plain); entry->bindString("name", Wt::WString::fromUTF8(displayInfo.title), Wt::TextFormat::Plain);
if (displayInfo.workName)
{
entry->setCondition("if-has-work", true);
entry->bindString("work", Wt::WString::fromUTF8(*displayInfo.workName), Wt::TextFormat::Plain);
}
const db::Release::pointer release{ track->getRelease() }; const db::Release::pointer release{ track->getRelease() };
const db::TrackId trackId{ track->getId() }; const db::TrackId trackId{ track->getId() };
@@ -270,7 +275,7 @@ namespace lms::ui::TrackListHelpers
entry->bindString("duration", utils::durationToString(track->getDuration()), Wt::TextFormat::Plain); entry->bindString("duration", utils::durationToString(track->getDuration()), Wt::TextFormat::Plain);
Wt::WPushButton* playBtn{ entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) }; Wt::WPushButton* playBtn{ entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) };
playBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.play-item").arg(displayTitle)); playBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.play-item").arg(displayInfo.title));
playBtn->clicked().connect([trackId, &playQueueController] { playBtn->clicked().connect([trackId, &playQueueController] {
db::TrackId trackIds[]{ trackId }; db::TrackId trackIds[]{ trackId };
playQueueController.processCommand(PlayQueueController::Command::Play, trackIds); playQueueController.processCommand(PlayQueueController::Command::Play, trackIds);
@@ -302,7 +307,7 @@ namespace lms::ui::TrackListHelpers
auto isStarred{ [=] { return core::Service<feedback::IFeedbackService>::get()->isStarred(LmsApp->getUserId(), trackId); } }; auto isStarred{ [=] { return core::Service<feedback::IFeedbackService>::get()->isStarred(LmsApp->getUserId(), trackId); } };
Wt::WPushButton* starBtn{ entry->bindNew<Wt::WPushButton>("star-btn", Wt::WString::tr(isStarred() ? "Lms.template.unstar-btn" : "Lms.template.star-btn"), Wt::TextFormat::XHTML) }; Wt::WPushButton* starBtn{ entry->bindNew<Wt::WPushButton>("star-btn", Wt::WString::tr(isStarred() ? "Lms.template.unstar-btn" : "Lms.template.star-btn"), Wt::TextFormat::XHTML) };
starBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.Explore.star-item").arg(displayTitle)); starBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.Explore.star-item").arg(displayInfo.title));
starBtn->setAttributeValue("aria-pressed", isStarred() ? "true" : "false"); starBtn->setAttributeValue("aria-pressed", isStarred() ? "true" : "false");
Wt::WPushButton* starMenuEntry{ entry->bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) }; Wt::WPushButton* starMenuEntry{ entry->bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) };