Added a user setting to chose darkmode or not

This commit is contained in:
emeric
2020-04-15 15:53:13 +02:00
parent c339d929d5
commit 4f560fe015
7 changed files with 118 additions and 55 deletions
+1 -1
View File
@@ -37,7 +37,7 @@
${tr:Lms.Auth.remember-me} ${tr:Lms.Auth.remember-me}
</label> </label>
<div class="col-sm-5"> <div class="col-sm-5">
${remember-me} <div class="checkbox"><label>${remember-me}</label></div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
+2
View File
@@ -155,8 +155,10 @@
<!--Settings--> <!--Settings-->
<message id="Lms.Settings.appearance">Appearance</message>
<message id="Lms.Settings.audio">Audio</message> <message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.change-password">Change password</message> <message id="Lms.Settings.change-password">Change password</message>
<message id="Lms.Settings.dark-mode">Dark mode</message>
<message id="Lms.Settings.demo-cannot-save">Cannot save using a demo account!</message> <message id="Lms.Settings.demo-cannot-save">Cannot save using a demo account!</message>
<message id="Lms.Settings.password-bad">Bad password</message> <message id="Lms.Settings.password-bad">Bad password</message>
<message id="Lms.Settings.password-must-fill-old-password">Old password must be filled in</message> <message id="Lms.Settings.password-must-fill-old-password">Old password must be filled in</message>
+17 -3
View File
@@ -4,16 +4,30 @@
<!--FORMS message blocks--> <!--FORMS message blocks-->
<message id="Lms.Settings.template"> <message id="Lms.Settings.template">
<legend>${tr:Lms.Settings.appearance}</legend>
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label" for="${id:dark-mode}">
${tr:Lms.Settings.dark-mode}
</label>
<div class="col-sm-5">
<div class="checkbox"><label>${dark-mode}</label></div>
</div>
<div class="col-sm-5 help-block">
${dark-mode-info}
</div>
</div>
</div>
<legend>${tr:Lms.Settings.audio}</legend> <legend>${tr:Lms.Settings.audio}</legend>
<div class="form-horizontal"> <div class="form-horizontal">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="${id:transcoding-enable}"> <label class="col-sm-2 control-label" for="${id:transcoding-enable}">
${tr:Lms.Settings.transcoding-enable} ${tr:Lms.Settings.transcoding-enable}
</label> </label>
<div class="col-sm-5"> <div class="col-sm-5">
${transcoding-enable} <div class="checkbox"><label>${transcoding-enable}</label></div>
</div> </div>
<div class="help-block col-sm-5"> <div class="col-sm-5 help-block">
${transcoding-enable-info} ${transcoding-enable-info}
</div> </div>
</div> </div>
+5 -1
View File
@@ -40,7 +40,7 @@
namespace Database { namespace Database {
#define LMS_DATABASE_VERSION 15 #define LMS_DATABASE_VERSION 16
using Version = std::size_t; using Version = std::size_t;
@@ -169,6 +169,10 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" (
// Just increment the scan version of the settings to make the next scheduled scan rescan everything // Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(*this).modify()->incScanVersion(); ScanSettings::get(*this).modify()->incScanVersion();
} }
else if (version == 15)
{
_session.execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultUITheme)) + ")");
}
else else
{ {
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
+22 -10
View File
@@ -91,14 +91,13 @@ class User : public Wt::Dbo::Dbo<User>
public: public:
using pointer = Wt::Dbo::ptr<User>; using pointer = Wt::Dbo::ptr<User>;
static const std::size_t MinNameLength = 3;
static const std::size_t MaxNameLength = 15;
// Do not change enum values!
enum class Type enum class Type
{ {
REGULAR, REGULAR = 0,
ADMIN, ADMIN = 1,
DEMO DEMO = 2,
}; };
struct PasswordHash struct PasswordHash
@@ -107,8 +106,21 @@ class User : public Wt::Dbo::Dbo<User>
std::string hash; std::string hash;
}; };
// list of audio parameters // Do not change enum values!
enum class UITheme
{
Light = 0,
Dark = 1,
};
static const std::set<Bitrate> audioTranscodeAllowedBitrates; static const std::set<Bitrate> audioTranscodeAllowedBitrates;
static inline const std::size_t MinNameLength {3};
static inline const std::size_t MaxNameLength {15};
static inline const bool defaultAudioTranscodeEnable {true};
static inline const AudioFormat defaultAudioTranscodeFormat {AudioFormat::OGG_OPUS};
static inline const Bitrate defaultAudioTranscodeBitrate {128000};
static inline const UITheme defaultUITheme {UITheme::Dark};
User(); User();
User(const std::string& loginName, const PasswordHash& passwordHash); User(const std::string& loginName, const PasswordHash& passwordHash);
@@ -138,6 +150,7 @@ class User : public Wt::Dbo::Dbo<User>
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
void setRadio(bool val) { _radio = val; } void setRadio(bool val) { _radio = val; }
void setRepeatAll(bool val) { _repeatAll = val; } void setRepeatAll(bool val) { _repeatAll = val; }
void setUITheme(UITheme uiTheme) { _uiTheme = uiTheme; }
void clearAuthTokens(); void clearAuthTokens();
// read // read
@@ -150,6 +163,7 @@ class User : public Wt::Dbo::Dbo<User>
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
bool isRepeatAllSet() const { return _repeatAll; } bool isRepeatAllSet() const { return _repeatAll; }
bool isRadioSet() const { return _radio; } bool isRadioSet() const { return _radio; }
UITheme getUITheme() const { return _uiTheme; }
Wt::Dbo::ptr<TrackList> getPlayedTrackList(Session& session) const; Wt::Dbo::ptr<TrackList> getPlayedTrackList(Session& session) const;
Wt::Dbo::ptr<TrackList> getQueuedTrackList(Session& session) const; Wt::Dbo::ptr<TrackList> getQueuedTrackList(Session& session) const;
@@ -184,6 +198,7 @@ class User : public Wt::Dbo::Dbo<User>
Wt::Dbo::field(a, _audioTranscodeEnable, "audio_transcode_enable"); Wt::Dbo::field(a, _audioTranscodeEnable, "audio_transcode_enable");
Wt::Dbo::field(a, _audioTranscodeBitrate, "audio_transcode_bitrate"); Wt::Dbo::field(a, _audioTranscodeBitrate, "audio_transcode_bitrate");
Wt::Dbo::field(a, _audioTranscodeFormat, "audio_transcode_format"); Wt::Dbo::field(a, _audioTranscodeFormat, "audio_transcode_format");
Wt::Dbo::field(a, _uiTheme, "ui_theme");
// User's dynamic data // User's dynamic data
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos"); Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
Wt::Dbo::field(a, _repeatAll, "repeat_all"); Wt::Dbo::field(a, _repeatAll, "repeat_all");
@@ -197,14 +212,11 @@ class User : public Wt::Dbo::Dbo<User>
private: private:
static const bool defaultAudioTranscodeEnable {true};
static const AudioFormat defaultAudioTranscodeFormat {AudioFormat::OGG_OPUS};
static const Bitrate defaultAudioTranscodeBitrate {128000};
std::string _loginName; std::string _loginName;
std::string _passwordSalt; std::string _passwordSalt;
std::string _passwordHash; std::string _passwordHash;
Wt::WDateTime _lastLogin; Wt::WDateTime _lastLogin;
UITheme _uiTheme {defaultUITheme};
// Admin defined settings // Admin defined settings
int _maxAudioTranscodeBitrate; int _maxAudioTranscodeBitrate;
+51 -24
View File
@@ -58,13 +58,47 @@
namespace UserInterface { namespace UserInterface {
class LmsBooststrapTheme : public Wt::WBootstrapTheme
{
public:
LmsBooststrapTheme(Database::User::UITheme theme) : _theme {theme} {}
private:
std::vector<Wt::WLinkedCssStyleSheet> styleSheets() const override
{
switch (_theme)
{
case Database::User::UITheme::Dark:
return
{
Wt::WLinkedCssStyleSheet {"css/bootstrap-darkly.min.css"},
Wt::WLinkedCssStyleSheet {"resources/themes/bootstrap/3/wt.css"},
Wt::WLinkedCssStyleSheet {"css/lms.css"},
Wt::WLinkedCssStyleSheet {"css/lms-darkly.css"},
};
case Database::User::UITheme::Light:
return
{
Wt::WLinkedCssStyleSheet {"css/bootstrap-flatly.min.css"},
Wt::WLinkedCssStyleSheet {"resources/themes/bootstrap/3/wt.css"},
Wt::WLinkedCssStyleSheet {"css/lms.css"},
Wt::WLinkedCssStyleSheet {"css/lms-flatly.css"},
};
}
return {};
}
Database::User::UITheme _theme;
};
std::unique_ptr<Wt::WApplication> std::unique_ptr<Wt::WApplication>
LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups) LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups)
{ {
return std::make_unique<LmsApplication>(env, db, appGroups); return std::make_unique<LmsApplication>(env, db, appGroups);
} }
LmsApplication* LmsApplication*
LmsApplication::instance() LmsApplication::instance()
{ {
return reinterpret_cast<LmsApplication*>(Wt::WApplication::instance()); return reinterpret_cast<LmsApplication*>(Wt::WApplication::instance());
@@ -116,31 +150,8 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env,
_dbSession {db}, _dbSession {db},
_appGroups {appGroups} _appGroups {appGroups}
{ {
class MyTheme : public Wt::WBootstrapTheme
{
private:
std::vector<Wt::WLinkedCssStyleSheet> styleSheets() const override
{
return
{
// Wt::WLinkedCssStyleSheet {Wt::WLink {"css/bootstrap-flatly.min.css"}},
Wt::WLinkedCssStyleSheet {Wt::WLink {"css/bootstrap-darkly.min.css"}},
Wt::WLinkedCssStyleSheet {Wt::WLink {"resources/themes/bootstrap/3/wt.css"}},
// Wt::WLinkedCssStyleSheet {Wt::WLink {"css/lms-flatly.css"}},
Wt::WLinkedCssStyleSheet {Wt::WLink {"css/lms-darkly.css"}},
};
}
};
auto bootstrapTheme = std::make_unique<MyTheme>();
bootstrapTheme->setVersion(Wt::BootstrapVersion::v3);
bootstrapTheme->setResponsive(true);
setTheme(std::move(bootstrapTheme));
addMetaHeader(Wt::MetaHeaderType::Meta, "viewport", "width=device-width, user-scalable=no"); addMetaHeader(Wt::MetaHeaderType::Meta, "viewport", "width=device-width, user-scalable=no");
useStyleSheet("css/lms.css");
useStyleSheet("resources/font-awesome/css/font-awesome.min.css"); useStyleSheet("resources/font-awesome/css/font-awesome.min.css");
// Add a resource bundle // Add a resource bundle
@@ -196,6 +207,22 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env,
} }
const auto userId {processAuthToken(env)}; const auto userId {processAuthToken(env)};
{
Database::User::UITheme theme {Database::User::defaultUITheme};
if (userId)
{
auto transaction {_dbSession.createSharedTransaction()};
const auto user {Database::User::getById(_dbSession, *userId)};
if (user)
theme = user->getUITheme();
}
auto LmsTheme {std::make_unique<LmsBooststrapTheme>(theme)};
LmsTheme->setVersion(Wt::BootstrapVersion::v3);
LmsTheme->setResponsive(true);
setTheme(std::move(LmsTheme));
}
if (userId) if (userId)
{ {
try try
+19 -15
View File
@@ -44,18 +44,20 @@ class SettingsModel : public Wt::WFormModel
{ {
public: public:
// Associate each field with a unique string literal. // Associate each field with a unique string literal.
static const Field TranscodeEnableField; static inline const Field DarkModeField {"dark-mode"};
static const Field TranscodeFormatField; static inline const Field TranscodeEnableField {"transcoding-enable"};
static const Field TranscodeBitrateField; static inline const Field TranscodeFormatField {"transcoding-bitrate"};
static const Field PasswordOldField; static inline const Field TranscodeBitrateField {"transcoding-format"};
static const Field PasswordField; static inline const Field PasswordOldField {"password-old"};
static const Field PasswordConfirmField; static inline const Field PasswordField {"password"};
static inline const Field PasswordConfirmField {"password-confirm"};
SettingsModel(bool withOldPassword) SettingsModel(bool withOldPassword)
: _withOldPassword {withOldPassword} : _withOldPassword {withOldPassword}
{ {
initializeModels(); initializeModels();
addField(DarkModeField);
addField(TranscodeEnableField); addField(TranscodeEnableField);
addField(TranscodeBitrateField); addField(TranscodeBitrateField);
addField(TranscodeFormatField); addField(TranscodeFormatField);
@@ -77,15 +79,16 @@ class SettingsModel : public Wt::WFormModel
void saveData() void saveData()
{ {
Database::User::PasswordHash passwordHash; User::PasswordHash passwordHash;
if (!valueText(PasswordField).empty()) if (!valueText(PasswordField).empty())
passwordHash = ServiceProvider<::Auth::IPasswordService>::get()->hashPassword(valueText(PasswordField).toUTF8()); passwordHash = ServiceProvider<::Auth::IPasswordService>::get()->hashPassword(valueText(PasswordField).toUTF8());
auto transaction {LmsApp->getDbSession().createUniqueTransaction()}; auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
Database::User::pointer user {LmsApp->getUser()}; User::pointer user {LmsApp->getUser()};
user.modify()->setUITheme(Wt::asNumber(value(DarkModeField)) ? User::UITheme::Dark : User::UITheme::Light);
user.modify()->setAudioTranscodeEnable(Wt::asNumber(value(TranscodeEnableField))); user.modify()->setAudioTranscodeEnable(Wt::asNumber(value(TranscodeEnableField)));
auto transcodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(TranscodeBitrateField))}; auto transcodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(TranscodeBitrateField))};
@@ -107,6 +110,8 @@ class SettingsModel : public Wt::WFormModel
{ {
auto transaction {LmsApp->getDbSession().createSharedTransaction()}; auto transaction {LmsApp->getDbSession().createSharedTransaction()};
setValue(DarkModeField, LmsApp->getUser()->getUITheme() == User::UITheme::Dark);
setValue(TranscodeEnableField, LmsApp->getUser()->getAudioTranscodeEnable()); setValue(TranscodeEnableField, LmsApp->getUser()->getAudioTranscodeEnable());
if (!LmsApp->getUser()->getAudioTranscodeEnable()) if (!LmsApp->getUser()->getAudioTranscodeEnable())
{ {
@@ -223,13 +228,6 @@ class SettingsModel : public Wt::WFormModel
std::shared_ptr<ValueStringModel<AudioFormat>> _transcodeFormatModel; std::shared_ptr<ValueStringModel<AudioFormat>> _transcodeFormatModel;
}; };
const Wt::WFormModel::Field SettingsModel::TranscodeEnableField = "transcoding-enable";
const Wt::WFormModel::Field SettingsModel::TranscodeBitrateField = "transcoding-bitrate";
const Wt::WFormModel::Field SettingsModel::TranscodeFormatField = "transcoding-format";
const Wt::WFormModel::Field SettingsModel::PasswordOldField = "password-old";
const Wt::WFormModel::Field SettingsModel::PasswordField = "password";
const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm";
SettingsView::SettingsView() SettingsView::SettingsView()
{ {
wApp->internalPathChanged().connect(std::bind([=] wApp->internalPathChanged().connect(std::bind([=]
@@ -252,6 +250,12 @@ SettingsView::refreshView()
auto model {std::make_shared<SettingsModel>(!LmsApp->isUserAuthStrong())}; auto model {std::make_shared<SettingsModel>(!LmsApp->isUserAuthStrong())};
// Appearance
{
auto darkMode {std::make_unique<Wt::WCheckBox>()};
t->setFormWidget(SettingsModel::DarkModeField, std::move(darkMode));
}
// Old password // Old password
if (!LmsApp->isUserAuthStrong()) if (!LmsApp->isUserAuthStrong())
{ {