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}
</label>
<div class="col-sm-5">
${remember-me}
<div class="checkbox"><label>${remember-me}</label></div>
</div>
</div>
<div class="form-group">
+2
View File
@@ -155,8 +155,10 @@
<!--Settings-->
<message id="Lms.Settings.appearance">Appearance</message>
<message id="Lms.Settings.audio">Audio</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.password-bad">Bad password</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-->
<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>
<div class="form-horizontal">
<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}
</label>
<div class="col-sm-5">
${transcoding-enable}
<div class="checkbox"><label>${transcoding-enable}</label></div>
</div>
<div class="help-block col-sm-5">
<div class="col-sm-5 help-block">
${transcoding-enable-info}
</div>
</div>
+5 -1
View File
@@ -40,7 +40,7 @@
namespace Database {
#define LMS_DATABASE_VERSION 15
#define LMS_DATABASE_VERSION 16
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
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
{
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
+23 -11
View File
@@ -91,14 +91,13 @@ class User : public Wt::Dbo::Dbo<User>
public:
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
{
REGULAR,
ADMIN,
DEMO
REGULAR = 0,
ADMIN = 1,
DEMO = 2,
};
struct PasswordHash
@@ -107,8 +106,21 @@ class User : public Wt::Dbo::Dbo<User>
std::string hash;
};
// list of audio parameters
static const std::set<Bitrate> audioTranscodeAllowedBitrates;
// Do not change enum values!
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(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 setRadio(bool val) { _radio = val; }
void setRepeatAll(bool val) { _repeatAll = val; }
void setUITheme(UITheme uiTheme) { _uiTheme = uiTheme; }
void clearAuthTokens();
// read
@@ -150,6 +163,7 @@ class User : public Wt::Dbo::Dbo<User>
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
bool isRepeatAllSet() const { return _repeatAll; }
bool isRadioSet() const { return _radio; }
UITheme getUITheme() const { return _uiTheme; }
Wt::Dbo::ptr<TrackList> getPlayedTrackList(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, _audioTranscodeBitrate, "audio_transcode_bitrate");
Wt::Dbo::field(a, _audioTranscodeFormat, "audio_transcode_format");
Wt::Dbo::field(a, _uiTheme, "ui_theme");
// User's dynamic data
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
Wt::Dbo::field(a, _repeatAll, "repeat_all");
@@ -197,14 +212,11 @@ class User : public Wt::Dbo::Dbo<User>
private:
static const bool defaultAudioTranscodeEnable {true};
static const AudioFormat defaultAudioTranscodeFormat {AudioFormat::OGG_OPUS};
static const Bitrate defaultAudioTranscodeBitrate {128000};
std::string _loginName;
std::string _passwordSalt;
std::string _passwordHash;
Wt::WDateTime _lastLogin;
UITheme _uiTheme {defaultUITheme};
// Admin defined settings
int _maxAudioTranscodeBitrate;
+51 -24
View File
@@ -58,13 +58,47 @@
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>
LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups)
{
return std::make_unique<LmsApplication>(env, db, appGroups);
}
LmsApplication*
LmsApplication*
LmsApplication::instance()
{
return reinterpret_cast<LmsApplication*>(Wt::WApplication::instance());
@@ -116,31 +150,8 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env,
_dbSession {db},
_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");
useStyleSheet("css/lms.css");
useStyleSheet("resources/font-awesome/css/font-awesome.min.css");
// Add a resource bundle
@@ -196,6 +207,22 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& 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)
{
try
+19 -15
View File
@@ -44,18 +44,20 @@ class SettingsModel : public Wt::WFormModel
{
public:
// Associate each field with a unique string literal.
static const Field TranscodeEnableField;
static const Field TranscodeFormatField;
static const Field TranscodeBitrateField;
static const Field PasswordOldField;
static const Field PasswordField;
static const Field PasswordConfirmField;
static inline const Field DarkModeField {"dark-mode"};
static inline const Field TranscodeEnableField {"transcoding-enable"};
static inline const Field TranscodeFormatField {"transcoding-bitrate"};
static inline const Field TranscodeBitrateField {"transcoding-format"};
static inline const Field PasswordOldField {"password-old"};
static inline const Field PasswordField {"password"};
static inline const Field PasswordConfirmField {"password-confirm"};
SettingsModel(bool withOldPassword)
: _withOldPassword {withOldPassword}
{
initializeModels();
addField(DarkModeField);
addField(TranscodeEnableField);
addField(TranscodeBitrateField);
addField(TranscodeFormatField);
@@ -77,15 +79,16 @@ class SettingsModel : public Wt::WFormModel
void saveData()
{
Database::User::PasswordHash passwordHash;
User::PasswordHash passwordHash;
if (!valueText(PasswordField).empty())
passwordHash = ServiceProvider<::Auth::IPasswordService>::get()->hashPassword(valueText(PasswordField).toUTF8());
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)));
auto transcodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(TranscodeBitrateField))};
@@ -107,6 +110,8 @@ class SettingsModel : public Wt::WFormModel
{
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
setValue(DarkModeField, LmsApp->getUser()->getUITheme() == User::UITheme::Dark);
setValue(TranscodeEnableField, LmsApp->getUser()->getAudioTranscodeEnable());
if (!LmsApp->getUser()->getAudioTranscodeEnable())
{
@@ -223,13 +228,6 @@ class SettingsModel : public Wt::WFormModel
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()
{
wApp->internalPathChanged().connect(std::bind([=]
@@ -252,6 +250,12 @@ SettingsView::refreshView()
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
if (!LmsApp->isUserAuthStrong())
{