Still converting to bootstrap 5. Made notification system work again

This commit is contained in:
emeric
2022-04-19 17:07:04 +02:00
parent a354e72896
commit 6046da7a4c
15 changed files with 140 additions and 53 deletions
+1
View File
@@ -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>
+14
View File
@@ -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>
+1
View File
@@ -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
+10 -34
View File
@@ -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
+4 -8
View File
@@ -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 {};
};
+11
View File
@@ -0,0 +1,11 @@
#pragma once
namespace UserInterface::Notification
{
enum class Type
{
Info,
Warning,
Danger,
};
}
+67
View File
@@ -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);
});
}
}
+18
View File
@@ -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
+2 -4
View File
@@ -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
+2 -2
View File
@@ -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.
+1 -1
View File
@@ -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.
+2 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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);
+3 -1
View File
@@ -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
+3 -1
View File
@@ -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();
}