Still converting to bootstrap 5. Made notification system work again
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
${navbar}
|
||||
<div class="container">
|
||||
${contents}
|
||||
${notifications class="toast-container position-fixed top-0 end-0 p-3"}
|
||||
</div>
|
||||
${player class="fixed-bottom bg-light Lms-player"}
|
||||
</message>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||
|
||||
<message id="Lms.notifications.template.entry">
|
||||
<div class="toast align-items-center ${bg-color} text-${text-color} border-0" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="${duration}">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
${message}
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-${text-color} me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
</messages>
|
||||
@@ -6,6 +6,7 @@ add_executable(lms
|
||||
ui/LmsApplicationManager.cpp
|
||||
ui/LmsTheme.cpp
|
||||
ui/MediaPlayer.cpp
|
||||
ui/NotificationContainer.cpp
|
||||
ui/PlayQueue.cpp
|
||||
ui/SettingsView.cpp
|
||||
ui/TrackStringUtils.cpp
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "LmsApplicationManager.hpp"
|
||||
#include "LmsTheme.hpp"
|
||||
#include "MediaPlayer.hpp"
|
||||
#include "NotificationContainer.hpp"
|
||||
#include "PlayQueue.hpp"
|
||||
#include "SettingsView.hpp"
|
||||
|
||||
@@ -186,6 +187,7 @@ LmsApplication::init()
|
||||
messageResourceBundle().use(appRoot() + "mediaplayer");
|
||||
messageResourceBundle().use(appRoot() + "messages");
|
||||
messageResourceBundle().use(appRoot() + "misc");
|
||||
messageResourceBundle().use(appRoot() + "notifications");
|
||||
messageResourceBundle().use(appRoot() + "playqueue");
|
||||
messageResourceBundle().use(appRoot() + "release");
|
||||
messageResourceBundle().use(appRoot() + "releases");
|
||||
@@ -460,6 +462,8 @@ LmsApplication::createHome()
|
||||
|
||||
Wt::WTemplate* navbar {main->bindNew<Wt::WTemplate>("navbar", Wt::WString::tr("Lms.main.template.navbar"))};
|
||||
|
||||
_notificationContainer = main->bindNew<NotificationContainer>("notifications");
|
||||
|
||||
// MediaPlayer
|
||||
_mediaPlayer = main->bindNew<MediaPlayer>("player");
|
||||
|
||||
@@ -566,7 +570,9 @@ LmsApplication::createHome()
|
||||
{
|
||||
_scannerEvents.scanComplete.connect([=] (const Scanner::ScanStats& stats)
|
||||
{
|
||||
notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-complete")
|
||||
notifyMsg(Notification::Type::Info,
|
||||
Wt::WString::tr("Lms.Admin.Database.database"),
|
||||
Wt::WString::tr("Lms.Admin.Database.scan-complete")
|
||||
.arg(static_cast<unsigned>(stats.nbFiles()))
|
||||
.arg(static_cast<unsigned>(stats.additions))
|
||||
.arg(static_cast<unsigned>(stats.updates))
|
||||
@@ -603,49 +609,19 @@ LmsApplication::notify(const Wt::WEvent& event)
|
||||
}
|
||||
}
|
||||
|
||||
static std::string msgTypeToString(LmsApplication::MsgType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case LmsApplication::MsgType::Success: return "success";
|
||||
case LmsApplication::MsgType::Info: return "info";
|
||||
case LmsApplication::MsgType::Warning: return "warning";
|
||||
case LmsApplication::MsgType::Danger: return "danger";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void
|
||||
LmsApplication::post(std::function<void()> func)
|
||||
{
|
||||
Wt::WServer::instance()->post(LmsApp->sessionId(), std::move(func));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LmsApplication::notifyMsg(MsgType type, const Wt::WString& message, std::chrono::milliseconds duration)
|
||||
LmsApplication::notifyMsg(Notification::Type type, const Wt::WString& category, const Wt::WString& message, std::chrono::milliseconds duration)
|
||||
{
|
||||
LMS_LOG(UI, INFO) << "Notifying message '" << message.toUTF8() << "' of type '" << msgTypeToString(type) << "'";
|
||||
|
||||
// TODO
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << "$.notify({"
|
||||
"message: '" << StringUtils::jsEscape(message.toUTF8()) << "'"
|
||||
"},{"
|
||||
"type: '" << msgTypeToString(type) << "',"
|
||||
"placement: {from: 'bottom', align: 'right'},"
|
||||
"timer: 250,"
|
||||
"offset: {x: 20, y: 80},"
|
||||
"delay: " << duration.count() << ""
|
||||
"});";
|
||||
|
||||
LmsApp->doJavaScript(oss.str());
|
||||
LMS_LOG(UI, INFO) << "Notifying message '" << message.toUTF8() << "' for category '" << category.toUTF8() << "'";
|
||||
_notificationContainer->add(type, category, message, duration);
|
||||
}
|
||||
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "services/database/UserId.hpp"
|
||||
#include "services/database/Types.hpp"
|
||||
#include "services/scanner/ScannerEvents.hpp"
|
||||
#include "Notification.hpp"
|
||||
|
||||
namespace Database
|
||||
{
|
||||
@@ -49,6 +50,7 @@ class LmsApplicationException;
|
||||
class MediaPlayer;
|
||||
class PlayQueue;
|
||||
class LmsApplicationManager;
|
||||
class NotificationContainer;
|
||||
|
||||
class LmsApplication : public Wt::WApplication
|
||||
{
|
||||
@@ -78,14 +80,7 @@ class LmsApplication : public Wt::WApplication
|
||||
void post(std::function<void()> func);
|
||||
|
||||
// Used to classify the message sent to the user
|
||||
enum class MsgType
|
||||
{
|
||||
Success,
|
||||
Info,
|
||||
Warning,
|
||||
Danger,
|
||||
};
|
||||
void notifyMsg(MsgType type, const Wt::WString& message, std::chrono::milliseconds duration = std::chrono::milliseconds {4000});
|
||||
void notifyMsg(Notification::Type type, const Wt::WString& category, const Wt::WString& message, std::chrono::milliseconds duration = std::chrono::milliseconds {5000});
|
||||
|
||||
static Wt::WLink createArtistLink(Database::ObjectPtr<Database::Artist> artist);
|
||||
static std::unique_ptr<Wt::WAnchor> createArtistAnchor(Database::ObjectPtr<Database::Artist> artist, bool addText = true);
|
||||
@@ -129,6 +124,7 @@ class LmsApplication : public Wt::WApplication
|
||||
MediaPlayer* _mediaPlayer {};
|
||||
PlayQueue* _playQueue {};
|
||||
std::unique_ptr<Wt::WPopupMenu> _popupMenu;
|
||||
NotificationContainer* _notificationContainer {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace UserInterface::Notification
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Danger,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "NotificationContainer.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <Wt/WTemplate.h>
|
||||
|
||||
#include "utils/Logger.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class NotificationWidget : public Wt::WTemplate
|
||||
{
|
||||
public:
|
||||
NotificationWidget(Notification::Type type, const Wt::WString& category, const Wt::WString& message, std::chrono::milliseconds duration);
|
||||
Wt::JSignal<> closed {this, "closed"};
|
||||
};
|
||||
|
||||
NotificationWidget::NotificationWidget(Notification::Type type, const Wt::WString& category, const Wt::WString& message, std::chrono::milliseconds duration)
|
||||
: Wt::WTemplate {Wt::WString::tr("Lms.notifications.template.entry")}
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Notification::Type::Info:
|
||||
bindString("bg-color", "bg-primary");
|
||||
bindString("text-color", "white");
|
||||
break;
|
||||
case Notification::Type::Warning:
|
||||
bindString("bg-color", "bg-warning");
|
||||
bindString("text-color", "dark");
|
||||
break;
|
||||
case Notification::Type::Danger:
|
||||
bindString("bg-color", "bg-danger");
|
||||
bindString("text-color", "white");
|
||||
break;
|
||||
}
|
||||
|
||||
bindString("category", category);
|
||||
bindString("message", message);
|
||||
bindInt("duration", duration.count());
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
oss
|
||||
<< R"({const toastElement = )" << jsRef() << R"(.getElementsByClassName('toast')[0];)"
|
||||
<< R"(const toast = bootstrap.Toast.getOrCreateInstance(toastElement);)"
|
||||
<< R"(toast.show();)"
|
||||
<< R"(toastElement.addEventListener('hidden.bs.toast', function () {)"
|
||||
<< closed.createCall({})
|
||||
<< R"(toast.dispose();)"
|
||||
<< R"(});})";
|
||||
|
||||
LMS_LOG(UI, DEBUG) << "Running JS '" << oss.str() << "'";
|
||||
|
||||
doJavaScript(oss.str());
|
||||
}
|
||||
|
||||
void
|
||||
NotificationContainer::add(Notification::Type type, const Wt::WString& category, const Wt::WString& message, std::chrono::milliseconds duration)
|
||||
{
|
||||
NotificationWidget* notification {addNew<NotificationWidget>(type, category, message, duration)};
|
||||
|
||||
notification->closed.connect([=]
|
||||
{
|
||||
removeWidget(notification);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <Wt/WContainerWidget.h>
|
||||
#include <Wt/WString.h>
|
||||
|
||||
#include "Notification.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class NotificationContainer : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
void add(Notification::Type type, const Wt::WString& category, const Wt::WString& message, std::chrono::milliseconds duration);
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace UserInterface
|
||||
@@ -383,12 +383,10 @@ PlayQueue::processTracks(PlayQueueAction action, const std::vector<Database::Tra
|
||||
}
|
||||
|
||||
if (nbAddedTracks > 0)
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", nbAddedTracks).arg(nbAddedTracks), std::chrono::milliseconds(2000));
|
||||
LmsApp->notifyMsg(Notification::Type::Info, Wt::WString::tr("Lms.PlayQueue.playqueue"), Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", nbAddedTracks).arg(nbAddedTracks), std::chrono::milliseconds(2000));
|
||||
|
||||
if (isFull())
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Warning, Wt::WString::tr("Lms.PlayQueue.playqueue-full"), std::chrono::milliseconds(2000));
|
||||
|
||||
|
||||
LmsApp->notifyMsg(Notification::Type::Warning, Wt::WString::tr("Lms.PlayQueue.playqueue"), Wt::WString::tr("Lms.PlayQueue.playqueue-full"), std::chrono::milliseconds(2000));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -564,7 +564,7 @@ SettingsView::refreshView()
|
||||
|
||||
if (LmsApp->getUser()->isDemo())
|
||||
{
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Warning, Wt::WString::tr("Lms.Settings.demo-cannot-save"));
|
||||
LmsApp->notifyMsg(Notification::Type::Warning, Wt::WString::tr("Lms.Settings.settings"), Wt::WString::tr("Lms.Settings.demo-cannot-save"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -574,7 +574,7 @@ SettingsView::refreshView()
|
||||
if (model->validate())
|
||||
{
|
||||
model->saveData();
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Settings.settings-saved"));
|
||||
LmsApp->notifyMsg(Notification::Type::Info, Wt::WString::tr("Lms.Settings.settings"), Wt::WString::tr("Lms.Settings.settings-saved"));
|
||||
}
|
||||
|
||||
// Udate the view: Delete any validation message in the view, etc.
|
||||
|
||||
@@ -241,7 +241,7 @@ DatabaseSettingsView::refreshView()
|
||||
model->saveData();
|
||||
|
||||
Service<Scanner::IScannerService>::get()->requestImmediateScan(false);
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Admin.Database.settings-saved"));
|
||||
LmsApp->notifyMsg(Notification::Type::Info, Wt::WString::tr("Lms.Admin.Database.database"), Wt::WString::tr("Lms.Admin.Database.settings-saved"));
|
||||
}
|
||||
|
||||
// Udate the view: Delete any validation message in the view, etc.
|
||||
|
||||
@@ -134,7 +134,8 @@ InitWizardView::InitWizardView()
|
||||
if (model->validate())
|
||||
{
|
||||
model->saveData();
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Admin.InitWizard.done"));
|
||||
// TODO notify here
|
||||
// LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr("Lms.Admin.InitWizard.done"));
|
||||
saveButton->setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ ScannerController::ScannerController()
|
||||
|
||||
LmsApp->getScannerEvents().scanStarted.connect(this, []
|
||||
{
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-launched"));
|
||||
LmsApp->notifyMsg(Notification::Type::Info, Wt::WString::tr("Lms.Admin.Database.database"), Wt::WString::tr("Lms.Admin.Database.scan-launched"));
|
||||
});
|
||||
LmsApp->getScannerEvents().scanComplete.connect(this, onDbEvent);
|
||||
LmsApp->getScannerEvents().scanInProgress.connect(this, onDbEvent);
|
||||
|
||||
@@ -252,7 +252,9 @@ UserView::refreshView()
|
||||
if (model->validate())
|
||||
{
|
||||
model->saveData();
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Success, Wt::WString::tr(userId ? "Lms.Admin.User.user-updated" : "Lms.Admin.User.user-created"));
|
||||
LmsApp->notifyMsg(Notification::Type::Info,
|
||||
Wt::WString::tr("Lms.Admin.Users.users"),
|
||||
Wt::WString::tr(userId ? "Lms.Admin.User.user-updated" : "Lms.Admin.User.user-created"));
|
||||
LmsApp->setInternalPath("/admin/users", true);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -155,7 +155,9 @@ Filters::add(ClusterId clusterId)
|
||||
_sigUpdated.emit();
|
||||
});
|
||||
|
||||
LmsApp->notifyMsg(LmsApplication::MsgType::Info, Wt::WString::tr("Lms.Explore.filter-added"), std::chrono::seconds {2});
|
||||
LmsApp->notifyMsg(Notification::Type::Info,
|
||||
Wt::WString::tr("Lms.Explore.filters"),
|
||||
Wt::WString::tr("Lms.Explore.filter-added"), std::chrono::seconds {2});
|
||||
|
||||
_sigUpdated.emit();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user