Added a user setting to chose darkmode or not
This commit is contained in:
+1
-1
@@ -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">
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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!
|
||||||
static const std::set<Bitrate> audioTranscodeAllowedBitrates;
|
enum class UITheme
|
||||||
|
{
|
||||||
|
Light = 0,
|
||||||
|
Dark = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|||||||
@@ -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
@@ -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())
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user