From c165ed9f2f897406338bb13e497a4397e261ebe8 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 12 Sep 2024 14:01:03 +0200 Subject: [PATCH 1/3] Made some UI states persistent across sessions, ref #484 --- src/libs/core/include/core/String.hpp | 23 +++-- src/libs/database/CMakeLists.txt | 1 + src/libs/database/impl/Migration.cpp | 19 +++- src/libs/database/impl/Session.cpp | 2 + src/libs/database/impl/UIState.cpp | 61 ++++++++++++ src/libs/database/impl/User.cpp | 1 + .../database/include/database/UIState.hpp | 68 ++++++++++++++ .../database/include/database/UIStateId.hpp | 24 +++++ src/libs/database/include/database/User.hpp | 11 +-- src/libs/database/test/Migration.cpp | 3 + src/lms/CMakeLists.txt | 1 + src/lms/ui/LmsApplication.cpp | 66 ++++++++----- src/lms/ui/LmsApplication.hpp | 17 ++-- src/lms/ui/PlayQueue.cpp | 25 ++--- src/lms/ui/SettingsView.cpp | 2 +- src/lms/ui/State.cpp | 92 +++++++++++++++++++ src/lms/ui/State.hpp | 60 ++++++++++++ src/lms/ui/explore/ArtistsView.cpp | 29 ++++-- src/lms/ui/explore/ArtistsView.hpp | 1 - src/lms/ui/explore/DropDownMenuSelector.hpp | 4 +- src/lms/ui/explore/Filters.cpp | 22 +++-- src/lms/ui/explore/ReleasesView.cpp | 15 ++- src/lms/ui/explore/TrackListsView.cpp | 22 +++-- src/lms/ui/explore/TrackListsView.hpp | 3 +- src/lms/ui/explore/TracksView.cpp | 15 ++- 25 files changed, 486 insertions(+), 101 deletions(-) create mode 100644 src/libs/database/impl/UIState.cpp create mode 100644 src/libs/database/include/database/UIState.hpp create mode 100644 src/libs/database/include/database/UIStateId.hpp create mode 100644 src/lms/ui/State.cpp create mode 100644 src/lms/ui/State.hpp diff --git a/src/libs/core/include/core/String.hpp b/src/libs/core/include/core/String.hpp index 590e0034..2e1a488b 100644 --- a/src/libs/core/include/core/String.hpp +++ b/src/libs/core/include/core/String.hpp @@ -67,14 +67,25 @@ namespace lms::core::stringUtils template [[nodiscard]] std::optional readAs(std::string_view str) { - T res; + if constexpr (std::is_enum_v) + { + using UnderlyingType = std::underlying_type_t; + std::optional underlyingValue{ readAs(str) }; + if (!underlyingValue) + return std::nullopt; - std::istringstream iss{ std::string{ str } }; - iss >> res; - if (iss.fail()) - return std::nullopt; + return static_cast(*underlyingValue); + } + else + { + T res; + std::istringstream iss{ std::string{ str } }; + iss >> res; + if (iss.fail()) + return std::nullopt; - return res; + return res; + } } template<> diff --git a/src/libs/database/CMakeLists.txt b/src/libs/database/CMakeLists.txt index 6e8abadf..1b840fa0 100644 --- a/src/libs/database/CMakeLists.txt +++ b/src/libs/database/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(lmsdatabase SHARED impl/Track.cpp impl/TrackBookmark.cpp impl/Types.cpp + impl/UIState.cpp impl/User.cpp impl/Utils.cpp ) diff --git a/src/libs/database/impl/Migration.cpp b/src/libs/database/impl/Migration.cpp index f108a5dc..6d109a59 100644 --- a/src/libs/database/impl/Migration.cpp +++ b/src/libs/database/impl/Migration.cpp @@ -35,7 +35,7 @@ namespace lms::db { namespace { - static constexpr Version LMS_DATABASE_VERSION{ 66 }; + static constexpr Version LMS_DATABASE_VERSION{ 67 }; } VersionInfo::VersionInfo() @@ -744,6 +744,22 @@ SELECT session.getDboSession()->execute("UPDATE scan_settings SET scan_version = scan_version + 1"); } + void migrateFromV66(Session& session) + { + // New way of handling UI settings + session.getDboSession()->execute(R"(CREATE TABLE IF NOT EXISTS "ui_state" ( + "id" integer primary key autoincrement, + "version" integer not null, + "item" text not null, + "value" text not null, + "user_id" bigint, + constraint "fk_ui_state_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred +))"); + + session.getDboSession()->execute("ALTER TABLE user DROP COLUMN repeat_all"); + session.getDboSession()->execute("ALTER TABLE user DROP COLUMN radio"); + } + bool doDbMigration(Session& session) { static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" }; @@ -786,6 +802,7 @@ SELECT { 63, migrateFromV63 }, { 64, migrateFromV64 }, { 65, migrateFromV65 }, + { 66, migrateFromV66 }, }; bool migrationPerformed{}; diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp index ca3d1616..14106053 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -44,6 +44,7 @@ #include "database/TrackFeatures.hpp" #include "database/TrackList.hpp" #include "database/TransactionChecker.hpp" +#include "database/UIState.hpp" #include "database/User.hpp" #include "EnumSetTraits.hpp" @@ -117,6 +118,7 @@ namespace lms::db _session.mapClass("track_features"); _session.mapClass("tracklist"); _session.mapClass("tracklist_entry"); + _session.mapClass("ui_state"); _session.mapClass("user"); } diff --git a/src/libs/database/impl/UIState.cpp b/src/libs/database/impl/UIState.cpp new file mode 100644 index 00000000..30abf96e --- /dev/null +++ b/src/libs/database/impl/UIState.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include "database/UIState.hpp" + +#include "core/ILogger.hpp" +#include "database/Session.hpp" +#include "database/User.hpp" + +#include "IdTypeTraits.hpp" +#include "StringViewTraits.hpp" +#include "Utils.hpp" + +namespace lms::db +{ + UIState::UIState(std::string_view item, ObjectPtr user) + : _item{ item } + , _user{ getDboPtr(user) } + { + } + + UIState::pointer UIState::create(Session& session, std::string_view item, ObjectPtr user) + { + return session.getDboSession()->add(std::unique_ptr{ new UIState{ item, user } }); + } + + std::size_t UIState::getCount(Session& session) + { + session.checkReadTransaction(); + + return utils::fetchQuerySingleResult(session.getDboSession()->query("SELECT COUNT(*) FROM ui_state")); + } + + UIState::pointer UIState::find(Session& session, UIStateId settingId) + { + auto query{ session.getDboSession()->query>("SELECT ui_s from ui_state ui_s").where("ui_s.id = ?").bind(settingId) }; + return utils::fetchQuerySingleResult(query); + } + + UIState::pointer UIState::find(Session& session, std::string_view item, UserId userId) + { + auto query{ session.getDboSession()->query>("SELECT ui_s from ui_state ui_s").where("ui_s.item = ?").bind(item).where("ui_s.user_id = ?").bind(userId) }; + return utils::fetchQuerySingleResult(query); + } +} // namespace lms::db \ No newline at end of file diff --git a/src/libs/database/impl/User.cpp b/src/libs/database/impl/User.cpp index 22dd0bbb..ecb11727 100644 --- a/src/libs/database/impl/User.cpp +++ b/src/libs/database/impl/User.cpp @@ -24,6 +24,7 @@ #include "database/Release.hpp" #include "database/Session.hpp" #include "database/Track.hpp" +#include "database/UIState.hpp" #include "IdTypeTraits.hpp" #include "StringViewTraits.hpp" diff --git a/src/libs/database/include/database/UIState.hpp b/src/libs/database/include/database/UIState.hpp new file mode 100644 index 00000000..06455269 --- /dev/null +++ b/src/libs/database/include/database/UIState.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include + +#include "core/String.hpp" +#include "database/Object.hpp" +#include "database/Types.hpp" +#include "database/UIStateId.hpp" +#include "database/UserId.hpp" + +namespace lms::db +{ + class User; + class Session; + + class UIState final : public Object + { + public: + UIState() = default; + + static std::size_t getCount(Session& session); + static pointer find(Session& session, UIStateId settingId); + static pointer find(Session& session, std::string_view item, UserId userId); + + // Getters + std::string_view getValue() const { return _value; }; + + // Setters + void setValue(std::string_view value) { _value = value; }; + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _item, "item"); + Wt::Dbo::field(a, _value, "value"); + + Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade); + } + + private: + friend class Session; + UIState(std::string_view item, ObjectPtr user); + static pointer create(Session& session, std::string_view item, ObjectPtr user); + + std::string _item; + std::string _value; + Wt::Dbo::ptr _user; + }; +} // namespace lms::db diff --git a/src/libs/database/include/database/UIStateId.hpp b/src/libs/database/include/database/UIStateId.hpp new file mode 100644 index 00000000..363624a7 --- /dev/null +++ b/src/libs/database/include/database/UIStateId.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include "database/IdType.hpp" + +LMS_DECLARE_IDTYPE(UIStateId) diff --git a/src/libs/database/include/database/User.hpp b/src/libs/database/include/database/User.hpp index 537ecd1e..eca63221 100644 --- a/src/libs/database/include/database/User.hpp +++ b/src/libs/database/include/database/User.hpp @@ -35,6 +35,7 @@ namespace lms::db { class AuthToken; class Session; + class UIState; class User final : public Object { @@ -105,8 +106,6 @@ namespace lms::db void setSubsonicDefaultTranscodintOutputFormat(TranscodingOutputFormat encoding) { _subsonicDefaultTranscodingOutputFormat = encoding; } void setSubsonicDefaultTranscodingOutputBitrate(Bitrate bitrate); 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(); void setSubsonicArtistListMode(SubsonicArtistListMode mode) { _subsonicArtistListMode = mode; } @@ -122,8 +121,6 @@ namespace lms::db TranscodingOutputFormat getSubsonicDefaultTranscodingOutputFormat() const { return _subsonicDefaultTranscodingOutputFormat; } Bitrate getSubsonicDefaultTranscodingOutputBitrate() const { return _subsonicDefaultTranscodingOutputBitrate; } std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } - bool isRepeatAllSet() const { return _repeatAll; } - bool isRadioSet() const { return _radio; } UITheme getUITheme() const { return _uiTheme; } SubsonicArtistListMode getSubsonicArtistListMode() const { return _subsonicArtistListMode; } FeedbackBackend getFeedbackBackend() const { return _feedbackBackend; } @@ -149,10 +146,9 @@ namespace lms::db // UI player settings Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos"); - Wt::Dbo::field(a, _repeatAll, "repeat_all"); - Wt::Dbo::field(a, _radio, "radio"); Wt::Dbo::hasMany(a, _authTokens, Wt::Dbo::ManyToOne, "user"); + Wt::Dbo::hasMany(a, _uiSettings, Wt::Dbo::ManyToOne, "user"); } private: @@ -180,10 +176,9 @@ namespace lms::db // User's dynamic data (UI) int _curPlayingTrackPos{}; // Current track position in queue - bool _repeatAll{}; - bool _radio{}; Wt::Dbo::collection> _authTokens; + Wt::Dbo::collection> _uiSettings; }; } // namespace lms::db diff --git a/src/libs/database/test/Migration.cpp b/src/libs/database/test/Migration.cpp index a9baad7f..763f8573 100644 --- a/src/libs/database/test/Migration.cpp +++ b/src/libs/database/test/Migration.cpp @@ -29,6 +29,8 @@ #include "database/StarredArtist.hpp" #include "database/StarredRelease.hpp" #include "database/StarredTrack.hpp" +#include "database/UIState.hpp" +#include "database/User.hpp" namespace lms::db::tests { @@ -349,6 +351,7 @@ VALUES EXPECT_FALSE(StarredTrack::find(session, StarredTrackId{})); EXPECT_FALSE(Track::find(session, TrackId{})); EXPECT_FALSE(TrackList::find(session, TrackListId{})); + EXPECT_FALSE(UIState::find(session, UIStateId{})); EXPECT_FALSE(User::find(session, UserId{})); } } diff --git a/src/lms/CMakeLists.txt b/src/lms/CMakeLists.txt index c67e9ce6..6a30b407 100644 --- a/src/lms/CMakeLists.txt +++ b/src/lms/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(lms ui/NotificationContainer.cpp ui/PlayQueue.cpp ui/SettingsView.cpp + ui/State.cpp ui/Utils.cpp ui/admin/InitWizardView.cpp ui/admin/MediaLibrariesView.cpp diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp index 8624bd38..767f466a 100644 --- a/src/lms/ui/LmsApplication.cpp +++ b/src/lms/ui/LmsApplication.cpp @@ -207,34 +207,34 @@ namespace lms::ui db::User::pointer LmsApplication::getUser() { - if (!_authenticatedUser) + if (!_user) return {}; - return db::User::find(getDbSession(), _authenticatedUser->userId); + return db::User::find(getDbSession(), _user->userId); } - db::UserId LmsApplication::getUserId() + db::UserId LmsApplication::getUserId() const { - return _authenticatedUser->userId; + assert(_user); + return _user->userId; } bool LmsApplication::isUserAuthStrong() const { - return _authenticatedUser->strongAuth; + assert(_user); + return _user->strongAuth; } - db::UserType LmsApplication::getUserType() + db::UserType LmsApplication::getUserType() const { - auto transaction{ getDbSession().createReadTransaction() }; - - return getUser()->getType(); + assert(_user); + return _user->userType; } - std::string LmsApplication::getUserLoginName() + std::string_view LmsApplication::getUserLoginName() const { - auto transaction{ getDbSession().createReadTransaction() }; - - return getUser()->getLoginName(); + assert(_user); + return _user->userLoginName; } LmsApplication::LmsApplication(const Wt::WEnvironment& env, @@ -244,11 +244,10 @@ namespace lms::ui : Wt::WApplication{ env } , _db{ db } , _appManager{ appManager } - , _authenticatedUser{ userId ? std::make_optional(UserAuthInfo{ *userId, false }) : std::nullopt } { try { - init(); + init(userId); } catch (LmsApplicationException& e) { @@ -264,7 +263,7 @@ namespace lms::ui LmsApplication::~LmsApplication() = default; - void LmsApplication::init() + void LmsApplication::init(std::optional userId) { LMS_SCOPED_TRACE_OVERVIEW("UI", "ApplicationInit"); @@ -279,8 +278,8 @@ namespace lms::ui // Handle Media Scanner events and other session events enableUpdates(true); - if (_authenticatedUser) - onUserLoggedIn(); + if (userId) + onUserLoggedIn(*userId, false /* strongAuth */); else if (core::Service::exists()) processPasswordAuth(); } @@ -292,8 +291,7 @@ namespace lms::ui if (userId) { LMS_LOG(UI, DEBUG, "User authenticated using Auth token!"); - _authenticatedUser = { *userId, false }; - onUserLoggedIn(); + onUserLoggedIn(*userId, false /* strongAuth */); return; } } @@ -315,15 +313,14 @@ namespace lms::ui { Auth* auth{ root()->addNew() }; auth->userLoggedIn.connect(this, [this](db::UserId userId) { - _authenticatedUser = { userId, true }; - onUserLoggedIn(); + onUserLoggedIn(userId, true /* strongAuth */); }); } } void LmsApplication::finalize() { - if (_authenticatedUser) + if (_user) _appManager.unregisterApplication(*this); preQuit().emit(); @@ -359,10 +356,12 @@ namespace lms::ui goHomeAndQuit(); } - void LmsApplication::onUserLoggedIn() + void LmsApplication::onUserLoggedIn(db::UserId userId, bool strongAuth) { root()->clear(); + setUserInfo(userId, strongAuth); + LMS_LOG(UI, INFO, "User '" << getUserLoginName() << "' logged in from '" << environment().clientAddress() << "', user agent = " << environment().userAgent()); _appManager.registerApplication(*this); @@ -380,6 +379,23 @@ namespace lms::ui createHome(); } + void LmsApplication::setUserInfo(db::UserId userId, bool strongAuth) + { + auto transaction{ getDbSession().createReadTransaction() }; + + const db::User::pointer user{ db::User::find(getDbSession(), userId) }; + if (!user) + throw core::LmsException{ "Internal error" }; // Do not put details here at it may appear on the user rendered html + + assert(!_user); + _user = UserAuthInfo{ + .userId = userId, + .userType = user->getType(), + .userLoginName = user->getLoginName(), + .strongAuth = strongAuth + }; + } + void LmsApplication::createHome() { LMS_SCOPED_TRACE_OVERVIEW("UI", "ApplicationCreateHome"); @@ -420,7 +436,7 @@ namespace lms::ui navbar->bindNew("tracklists", Wt::WLink{ Wt::LinkType::InternalPath, "/tracklists" }, Wt::WString::tr("Lms.Explore.tracklists")); Filters* filters{ navbar->bindNew("filters") }; - navbar->bindString("username", getUserLoginName(), Wt::TextFormat::Plain); + navbar->bindString("username", std::string{ getUserLoginName() }, Wt::TextFormat::Plain); navbar->bindNew("settings", Wt::WLink{ Wt::LinkType::InternalPath, "/settings" }, Wt::WString::tr("Lms.Settings.menu-settings")); { diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index 141e442c..aeb3df94 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -20,6 +20,8 @@ #pragma once #include +#include +#include #include @@ -63,10 +65,10 @@ namespace lms::ui db::Session& getDbSession(); // always thread safe db::ObjectPtr getUser(); - db::UserId getUserId(); + db::UserId getUserId() const; bool isUserAuthStrong() const; // user must be logged in prior this call - db::UserType getUserType(); // user must be logged in prior this call - std::string getUserLoginName(); // user must be logged in prior this call + db::UserType getUserType() const; // user must be logged in prior this call + std::string_view getUserLoginName() const; // user must be logged in prior this call // Proxified scanner events scanner::Events& getScannerEvents() { return _scannerEvents; } @@ -86,18 +88,19 @@ namespace lms::ui Wt::Signal<>& preQuit() { return _preQuit; } private: - void init(); + void init(std::optional userId); void processPasswordAuth(); void handleException(LmsApplicationException& e); void goHomeAndQuit(); // Signal slots void logoutUser(); - void onUserLoggedIn(); + void onUserLoggedIn(db::UserId userId, bool strongAuth); void notify(const Wt::WEvent& event) override; void finalize() override; + void setUserInfo(db::UserId userId, bool strongAuth); void createHome(); db::Db& _db; @@ -107,9 +110,11 @@ namespace lms::ui struct UserAuthInfo { db::UserId userId; + db::UserType userType{ db::UserType::REGULAR }; + std::string userLoginName; bool strongAuth{}; }; - std::optional _authenticatedUser; + std::optional _user; std::shared_ptr _coverResource; MediaPlayer* _mediaPlayer{}; PlayQueue* _playQueue{}; diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 3ef281cd..a1087b70 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -45,6 +45,7 @@ #include "LmsApplication.hpp" #include "MediaPlayer.hpp" #include "ModalManager.hpp" +#include "State.hpp" #include "Utils.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "common/MandatoryValidator.hpp" @@ -156,35 +157,21 @@ namespace lms::ui _repeatBtn = bindNew("repeat-btn"); _repeatBtn->clicked().connect([this] { - auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; - - if (!LmsApp->getUser()->isDemo()) - LmsApp->getUser().modify()->setRepeatAll(isRepeatAllSet()); + state::writeValue("player_repeat_all", isRepeatAllSet()); }); - { - auto transaction{ LmsApp->getDbSession().createReadTransaction() }; - if (LmsApp->getUser()->isRepeatAllSet()) - _repeatBtn->setCheckState(Wt::CheckState::Checked); - } + if (state::readValue("player_repeat_all").value_or(false)) + _repeatBtn->setCheckState(Wt::CheckState::Checked); _radioBtn = bindNew("radio-btn"); _radioBtn->clicked().connect([this] { { - auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; - - if (!LmsApp->getUser()->isDemo()) - LmsApp->getUser().modify()->setRadio(isRadioModeSet()); + state::writeValue("player_radio_mode", isRadioModeSet()); } if (isRadioModeSet()) enqueueRadioTracksIfNeeded(); }); - bool isRadioModeSet{}; - { - auto transaction{ LmsApp->getDbSession().createReadTransaction() }; - isRadioModeSet = LmsApp->getUser()->isRadioSet(); - } - if (isRadioModeSet) + if (state::readValue("player_radio_mode").value_or(false)) { _radioBtn->setCheckState(Wt::CheckState::Checked); enqueueRadioTracksIfNeeded(); diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index f82c8445..68b644e7 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -102,7 +102,7 @@ namespace lms::ui } addField(PasswordField); - setValidator(PasswordField, createPasswordStrengthValidator([] { return auth::PasswordValidationContext{ LmsApp->getUserLoginName(), LmsApp->getUserType() }; })); + setValidator(PasswordField, createPasswordStrengthValidator([] { return auth::PasswordValidationContext{ .loginName = std::string{ LmsApp->getUserLoginName() }, .userType = LmsApp->getUserType() }; })); addField(PasswordConfirmField); } diff --git a/src/lms/ui/State.cpp b/src/lms/ui/State.cpp new file mode 100644 index 00000000..5818a39c --- /dev/null +++ b/src/lms/ui/State.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#include "State.hpp" + +#include "core/ILogger.hpp" +#include "database/Session.hpp" +#include "database/UIState.hpp" +#include "database/User.hpp" + +#include "LmsApplication.hpp" + +namespace lms::ui::state::details +{ + void writeValue(std::string_view item, std::string_view value) + { + // No UI state stored for demo user + if (LmsApp->getUserType() == db::UserType::DEMO) + return; + + LMS_LOG(UI, DEBUG, "Write state '" << item << "': '" << value << "'"); + + { + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; + + db::UIState::pointer state{ db::UIState::find(LmsApp->getDbSession(), item, LmsApp->getUserId()) }; + if (!state) + { + if (db::User::pointer user{ LmsApp->getUser() }) + state = LmsApp->getDbSession().create(item, user); + } + + if (state) + state.modify()->setValue(value); + } + } + + std::optional readValue(std::string_view item) + { + std::optional res; + + // No UI state stored for demo user + if (LmsApp->getUserType() == db::UserType::DEMO) + return res; + + { + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; + + const db::UIState::pointer state{ db::UIState::find(LmsApp->getDbSession(), item, LmsApp->getUserId()) }; + if (state) + res = state->getValue(); + } + + LMS_LOG(UI, DEBUG, "Read state '" << item << "': '" << (res ? *res : "") << "'";); + + return res; + } + + void eraseValue(std::string_view item) + { + // No UI state stored for demo user + if (LmsApp->getUserType() == db::UserType::DEMO) + return; + + LMS_LOG(UI, DEBUG, "Removing state '" << item << "'"); + + { + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; + + db::UIState::pointer state{ db::UIState::find(LmsApp->getDbSession(), item, LmsApp->getUserId()) }; + if (state) + state.remove(); + } + } + +} // namespace lms::ui::state::details diff --git a/src/lms/ui/State.hpp b/src/lms/ui/State.hpp new file mode 100644 index 00000000..603233ab --- /dev/null +++ b/src/lms/ui/State.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 Emeric Poupon + * + * This file is part of LMS. + * + * LMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LMS. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "core/String.hpp" + +namespace lms::ui::state +{ + namespace details + { + std::optional readValue(std::string_view item); + void writeValue(std::string_view item, std::string_view value); + void eraseValue(std::string_view item); + } // namespace details + + template + void writeValue(std::string_view item, std::optional value) + { + if (value.has_value()) + { + if constexpr (std::is_enum_v) + details::writeValue(item, std::to_string(static_cast>(*value))); + else + details::writeValue(item, std::to_string(*value)); + } + else + details::eraseValue(item); + } + + template + std::optional readValue(std::string_view item) + { + if (std::optional res{ details::readValue(item) }) + return core::stringUtils::readAs(*res); + + return std::nullopt; + } + +} // namespace lms::ui::state diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp index 5bce4d67..fab15863 100644 --- a/src/lms/ui/explore/ArtistsView.cpp +++ b/src/lms/ui/explore/ArtistsView.cpp @@ -30,6 +30,7 @@ #include "Filters.hpp" #include "LmsApplication.hpp" #include "SortModeSelector.hpp" +#include "State.hpp" #include "TrackArtistLinkTypeSelector.hpp" #include "common/InfiniteScrollingContainer.hpp" @@ -50,15 +51,27 @@ namespace lms::ui refreshView(searEdit->text()); }); - SortModeSelector* sortModeSelector{ bindNew("sort-mode", _defaultSortMode) }; - sortModeSelector->itemSelected.connect([this](ArtistCollector::Mode sortMode) { - refreshView(sortMode); - }); + { + const ArtistCollector::Mode sortMode{ state::readValue("artists_sort_mode").value_or(_defaultSortMode) }; + _artistCollector.setMode(sortMode); - TrackArtistLinkTypeSelector* linkTypeSelector{ bindNew("link-type", _defaultLinkType) }; - linkTypeSelector->itemSelected.connect([this](std::optional linkType) { - refreshView(linkType); - }); + SortModeSelector* sortModeSelector{ bindNew("sort-mode", sortMode) }; + sortModeSelector->itemSelected.connect([this](ArtistCollector::Mode newSortMode) { + state::writeValue("artists_sort_mode", newSortMode); + refreshView(newSortMode); + }); + } + + { + const std::optional linkType{ state::readValue("artists_link_type") }; + _artistCollector.setArtistLinkType(linkType); + + TrackArtistLinkTypeSelector* linkTypeSelector{ bindNew("link-type", linkType) }; + linkTypeSelector->itemSelected.connect([this](std::optional newLinkType) { + state::writeValue("artists_link_type", newLinkType); + refreshView(newLinkType); + }); + } _container = bindNew("artists", Wt::WString::tr("Lms.Explore.Artists.template.container")); _container->onRequestElements.connect([this] { diff --git a/src/lms/ui/explore/ArtistsView.hpp b/src/lms/ui/explore/ArtistsView.hpp index d22c0a93..e00546f8 100644 --- a/src/lms/ui/explore/ArtistsView.hpp +++ b/src/lms/ui/explore/ArtistsView.hpp @@ -54,6 +54,5 @@ namespace lms::ui InfiniteScrollingContainer* _container{}; ArtistCollector _artistCollector; static constexpr ArtistCollector::Mode _defaultSortMode{ ArtistCollector::Mode::Random }; - static constexpr std::optional _defaultLinkType{ std::nullopt }; }; } // namespace lms::ui diff --git a/src/lms/ui/explore/DropDownMenuSelector.hpp b/src/lms/ui/explore/DropDownMenuSelector.hpp index 43b930f2..0fbd5229 100644 --- a/src/lms/ui/explore/DropDownMenuSelector.hpp +++ b/src/lms/ui/explore/DropDownMenuSelector.hpp @@ -45,7 +45,9 @@ namespace lms::ui { auto* menuItem{ bindNew(var, title) }; menuItem->clicked().connect([this, menuItem, title, item] { - _currentActiveItem->removeStyleClass("active"); + if (_currentActiveItem) + _currentActiveItem->removeStyleClass("active"); + menuItem->addStyleClass("active"); _currentActiveItem = menuItem; _selectedItem->setText(title); diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp index cc8bd236..1529e47c 100644 --- a/src/lms/ui/explore/Filters.cpp +++ b/src/lms/ui/explore/Filters.cpp @@ -32,6 +32,7 @@ #include "LmsApplication.hpp" #include "ModalManager.hpp" +#include "State.hpp" #include "Utils.hpp" #include "common/ValueStringModel.hpp" @@ -54,7 +55,7 @@ namespace lms::ui auto transaction{ LmsApp->getDbSession().createReadTransaction() }; db::ClusterType::find(LmsApp->getDbSession(), [&](const db::ClusterType::pointer& clusterType) { typeModel->add(Wt::WString::fromUTF8(std::string{ clusterType->getName() }), clusterType->getId()); - }); + }); } typeModel->add(Wt::WString::tr("Lms.Explore.media-library"), MediaLibraryTag{}); @@ -77,7 +78,7 @@ namespace lms::ui { db::MediaLibrary::find(session, [&](const db::MediaLibrary::pointer& library) { valueModel->add(Wt::WString::fromUTF8(std::string{ library->getName() }), library->getId()); - }); + }); } else if (const db::ClusterTypeId * clusterTypeId{ std::get_if(&type) }) { @@ -87,7 +88,7 @@ namespace lms::ui db::Cluster::find(session, params, [&](const db::Cluster::pointer& cluster) { valueModel->add(Wt::WString::fromUTF8(std::string{ cluster->getName() }), cluster->getId()); - }); + }); } return valueModel; @@ -115,6 +116,7 @@ namespace lms::ui if (const db::MediaLibraryId * mediaLibraryId{ std::get_if(&value) }) { set(*mediaLibraryId); + state::writeValue("filters_media_library_id", mediaLibraryId->getValue()); } else if (const db::ClusterId * clusterId{ std::get_if(&value) }) { @@ -123,12 +125,12 @@ namespace lms::ui // TODO LmsApp->getModalManager().dispose(dialogPtr); - }); + }); Wt::WPushButton* cancelBtn{ dialog->bindNew("cancel-btn", Wt::WString::tr("Lms.cancel")) }; cancelBtn->clicked().connect([=] { LmsApp->getModalManager().dispose(dialogPtr); - }); + }); typeCombo->activated().connect([valueCombo, typeModel](int row) { const TypeVariant type{ typeModel->getValue(row) }; @@ -136,7 +138,7 @@ namespace lms::ui const std::shared_ptr valueModel{ createValueModel(type) }; valueCombo->clear(); valueCombo->setModel(valueModel); - }); + }); typeCombo->activated().emit(0); // force emit to refresh the type combo model @@ -153,6 +155,9 @@ namespace lms::ui addFilterBtn->clicked().connect(this, &Filters::showDialog); _filters = bindNew("clusters"); + + if (const std::optional mediaLibraryId{ state::readValue("filters_media_library_id") }) + set(*mediaLibraryId); } void Filters::add(db::ClusterId clusterId) @@ -176,7 +181,7 @@ namespace lms::ui _filters->removeWidget(filter); _clusterIds.erase(std::remove_if(std::begin(_clusterIds), std::end(_clusterIds), [clusterId](db::ClusterId id) { return id == clusterId; }), std::end(_clusterIds)); _sigUpdated.emit(); - }); + }); emitFilterAddedNotification(); } @@ -208,7 +213,8 @@ namespace lms::ui _mediaLibraryId = db::MediaLibraryId{}; _mediaLibraryFilter = nullptr; _sigUpdated.emit(); - }); + state::writeValue("filters_media_library_id", std::nullopt); + }); emitFilterAddedNotification(); } diff --git a/src/lms/ui/explore/ReleasesView.cpp b/src/lms/ui/explore/ReleasesView.cpp index 1d04244b..527df48f 100644 --- a/src/lms/ui/explore/ReleasesView.cpp +++ b/src/lms/ui/explore/ReleasesView.cpp @@ -27,6 +27,7 @@ #include "LmsApplication.hpp" #include "SortModeSelector.hpp" +#include "State.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "common/Template.hpp" #include "explore/Filters.hpp" @@ -51,10 +52,16 @@ namespace lms::ui refreshView(searEdit->text()); }); - SortModeSelector* sortMode{ bindNew("sort-mode", _defaultMode) }; - sortMode->itemSelected.connect([this](ReleaseCollector::Mode sortMode) { - refreshView(sortMode); - }); + { + const ReleaseCollector::Mode sortMode{ state::readValue("releases_sort_mode").value_or(_defaultMode) }; + _releaseCollector.setMode(sortMode); + + SortModeSelector* sortModeSelector{ bindNew("sort-mode", sortMode) }; + sortModeSelector->itemSelected.connect([this](ReleaseCollector::Mode newSortMode) { + state::writeValue("releases_sort_mode", newSortMode); + refreshView(newSortMode); + }); + } Wt::WPushButton* playBtn{ bindNew("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML) }; playBtn->clicked().connect([this] { diff --git a/src/lms/ui/explore/TrackListsView.cpp b/src/lms/ui/explore/TrackListsView.cpp index 163e13ea..c4c8f1e2 100644 --- a/src/lms/ui/explore/TrackListsView.cpp +++ b/src/lms/ui/explore/TrackListsView.cpp @@ -27,6 +27,7 @@ #include "DropDownMenuSelector.hpp" #include "Filters.hpp" #include "LmsApplication.hpp" +#include "State.hpp" #include "Utils.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "common/Template.hpp" @@ -42,15 +43,20 @@ namespace lms::ui addFunction("tr", &Wt::WTemplate::Functions::tr); addFunction("id", &Wt::WTemplate::Functions::id); - using SortModeSelector = DropDownMenuSelector; - SortModeSelector* sortModeSelector{ bindNew("sort-mode", Wt::WString::tr("Lms.Explore.TrackLists.template.sort-mode"), _mode) }; - sortModeSelector->bindItem("recently-modified", Wt::WString::tr("Lms.Explore.recently-modified"), Mode::RecentlyModified); - sortModeSelector->bindItem("all", Wt::WString::tr("Lms.Explore.all"), Mode::All); + { + _mode = state::readValue("tracklists_sort_mode").value_or(_defaultMode); - sortModeSelector->itemSelected.connect(this, [this](TrackLists::Mode mode) { - _mode = mode; - refreshView(); - }); + using SortModeSelector = DropDownMenuSelector; + SortModeSelector* sortModeSelector{ bindNew("sort-mode", Wt::WString::tr("Lms.Explore.TrackLists.template.sort-mode"), _mode) }; + sortModeSelector->bindItem("recently-modified", Wt::WString::tr("Lms.Explore.recently-modified"), Mode::RecentlyModified); + sortModeSelector->bindItem("all", Wt::WString::tr("Lms.Explore.all"), Mode::All); + + sortModeSelector->itemSelected.connect(this, [this](TrackLists::Mode mode) { + state::writeValue("tracklists_sort_mode", mode); + _mode = mode; + refreshView(); + }); + } _container = bindNew("tracklists", Wt::WString::tr("Lms.Explore.TrackLists.template.container")); _container->onRequestElements.connect([this] { diff --git a/src/lms/ui/explore/TrackListsView.hpp b/src/lms/ui/explore/TrackListsView.hpp index a0f4792c..e169203c 100644 --- a/src/lms/ui/explore/TrackListsView.hpp +++ b/src/lms/ui/explore/TrackListsView.hpp @@ -57,10 +57,11 @@ namespace lms::ui void addSome(); void addTracklist(const db::ObjectPtr& trackList); + static constexpr Mode _defaultMode{ Mode::RecentlyModified }; static constexpr std::size_t _batchSize{ 30 }; static constexpr std::size_t _maxCount{ 500 }; - Mode _mode{ Mode::RecentlyModified }; + Mode _mode; Filters& _filters; Wt::WWidget* _currentActiveItem{}; InfiniteScrollingContainer* _container{}; diff --git a/src/lms/ui/explore/TracksView.cpp b/src/lms/ui/explore/TracksView.cpp index 1d8b3d8d..63b1b306 100644 --- a/src/lms/ui/explore/TracksView.cpp +++ b/src/lms/ui/explore/TracksView.cpp @@ -28,6 +28,7 @@ #include "LmsApplication.hpp" #include "SortModeSelector.hpp" +#include "State.hpp" #include "common/InfiniteScrollingContainer.hpp" #include "explore/Filters.hpp" #include "explore/PlayQueueController.hpp" @@ -52,10 +53,16 @@ namespace lms::ui refreshView(searEdit->text()); }); - SortModeSelector* sortMode{ bindNew("sort-mode", _defaultMode) }; - sortMode->itemSelected.connect([this](TrackCollector::Mode mode) { - refreshView(mode); - }); + { + const TrackCollector::Mode sortMode{ state::readValue("tracks_sort_mode").value_or(_defaultMode) }; + _trackCollector.setMode(sortMode); + + SortModeSelector* sortModeSelector{ bindNew("sort-mode", sortMode) }; + sortModeSelector->itemSelected.connect([this](TrackCollector::Mode newSortMode) { + state::writeValue("tracks_sort_mode", newSortMode); + refreshView(newSortMode); + }); + } bindNew("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML) ->clicked() From cb182c3cfe39336f014d0dbf77a805f33b31e6a8 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 13 Sep 2024 17:47:53 +0200 Subject: [PATCH 2/3] Migrated current play counter in ui states --- src/libs/database/impl/Migration.cpp | 1 + src/libs/database/include/database/User.hpp | 12 ++-------- src/lms/ui/PlayQueue.cpp | 26 +++++++-------------- src/lms/ui/SettingsView.cpp | 4 +--- 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/libs/database/impl/Migration.cpp b/src/libs/database/impl/Migration.cpp index 6d109a59..b75f5c4b 100644 --- a/src/libs/database/impl/Migration.cpp +++ b/src/libs/database/impl/Migration.cpp @@ -758,6 +758,7 @@ SELECT session.getDboSession()->execute("ALTER TABLE user DROP COLUMN repeat_all"); session.getDboSession()->execute("ALTER TABLE user DROP COLUMN radio"); + session.getDboSession()->execute("ALTER TABLE user DROP COLUMN cur_playing_track_pos"); } bool doDbMigration(Session& session) diff --git a/src/libs/database/include/database/User.hpp b/src/libs/database/include/database/User.hpp index eca63221..f1ea2cb3 100644 --- a/src/libs/database/include/database/User.hpp +++ b/src/libs/database/include/database/User.hpp @@ -105,7 +105,6 @@ namespace lms::db void setSubsonicEnableTranscodingByDefault(bool value) { _subsonicEnableTranscodingByDefault = value; } void setSubsonicDefaultTranscodintOutputFormat(TranscodingOutputFormat encoding) { _subsonicDefaultTranscodingOutputFormat = encoding; } void setSubsonicDefaultTranscodingOutputBitrate(Bitrate bitrate); - void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } void setUITheme(UITheme uiTheme) { _uiTheme = uiTheme; } void clearAuthTokens(); void setSubsonicArtistListMode(SubsonicArtistListMode mode) { _subsonicArtistListMode = mode; } @@ -120,7 +119,6 @@ namespace lms::db bool getSubsonicEnableTranscodingByDefault() const { return _subsonicEnableTranscodingByDefault; } TranscodingOutputFormat getSubsonicDefaultTranscodingOutputFormat() const { return _subsonicDefaultTranscodingOutputFormat; } Bitrate getSubsonicDefaultTranscodingOutputBitrate() const { return _subsonicDefaultTranscodingOutputBitrate; } - std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } UITheme getUITheme() const { return _uiTheme; } SubsonicArtistListMode getSubsonicArtistListMode() const { return _subsonicArtistListMode; } FeedbackBackend getFeedbackBackend() const { return _feedbackBackend; } @@ -144,11 +142,8 @@ namespace lms::db Wt::Dbo::field(a, _scrobblingBackend, "scrobbling_backend"); Wt::Dbo::field(a, _listenbrainzToken, "listenbrainz_token"); - // UI player settings - Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos"); - Wt::Dbo::hasMany(a, _authTokens, Wt::Dbo::ManyToOne, "user"); - Wt::Dbo::hasMany(a, _uiSettings, Wt::Dbo::ManyToOne, "user"); + Wt::Dbo::hasMany(a, _uiStates, Wt::Dbo::ManyToOne, "user"); } private: @@ -174,11 +169,8 @@ namespace lms::db TranscodingOutputFormat _subsonicDefaultTranscodingOutputFormat{ defaultSubsonicTranscodingOutputFormat }; int _subsonicDefaultTranscodingOutputBitrate{ defaultSubsonicTranscodingOutputBitrate }; - // User's dynamic data (UI) - int _curPlayingTrackPos{}; // Current track position in queue - Wt::Dbo::collection> _authTokens; - Wt::Dbo::collection> _uiSettings; + Wt::Dbo::collection> _uiStates; }; } // namespace lms::db diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index a1087b70..0973f693 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -186,21 +186,15 @@ namespace lms::ui _mediaPlayerSettingsLoaded = true; - std::size_t trackPos{}; - - { - auto transaction{ LmsApp->getDbSession().createReadTransaction() }; - trackPos = LmsApp->getUser()->getCurPlayingTrackPos(); - } - + const std::size_t trackPos{ state::readValue("player_cur_playing_track_pos").value_or(0) }; loadTrack(trackPos, false); }); LmsApp->preQuit().connect([this] { - auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; - - if (LmsApp->getUser()->isDemo()) + if (LmsApp->getUserType() == db::UserType::DEMO) { + auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; + LMS_LOG(UI, DEBUG, "Removing queue (tracklist id " << _queueId.toString() << ")"); if (db::TrackList::pointer queue{ getQueue() }) queue.remove(); @@ -258,7 +252,7 @@ namespace lms::ui db::TrackId trackId{}; std::optional replayGain{}; { - auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; + auto transaction{ LmsApp->getDbSession().createReadTransaction() }; const db::TrackList::pointer queue{ getQueue() }; @@ -275,16 +269,14 @@ namespace lms::ui } _trackPos = pos; + const db::Track::pointer track{ queue->getEntry(*_trackPos)->getTrack() }; - trackId = track->getId(); - replayGain = getReplayGain(pos, track); - - if (!LmsApp->getUser()->isDemo()) - LmsApp->getUser().modify()->setCurPlayingTrackPos(pos); } + state::writeValue("player_cur_playing_track_pos", pos); + enqueueRadioTracksIfNeeded(); updateCurrentTrack(true); _isTrackSelected = true; @@ -331,7 +323,7 @@ namespace lms::ui db::TrackList::pointer queue; db::TrackList::pointer radioStartingTracks; - if (!LmsApp->getUser()->isDemo()) + if (LmsApp->getUserType() != db::UserType::DEMO) { static const std::string queueName{ "__queued_tracks__" }; queue = db::TrackList::find(LmsApp->getDbSession(), queueName, db::TrackListType::Internal, LmsApp->getUserId()); diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp index 68b644e7..27ba0247 100644 --- a/src/lms/ui/SettingsView.cpp +++ b/src/lms/ui/SettingsView.cpp @@ -547,9 +547,7 @@ namespace lms::ui saveBtn->clicked().connect([=] { { - auto transaction{ LmsApp->getDbSession().createReadTransaction() }; - - if (LmsApp->getUser()->isDemo()) + if (LmsApp->getUserType() == db::UserType::DEMO) { LmsApp->notifyMsg(Notification::Type::Warning, Wt::WString::tr("Lms.Settings.settings"), Wt::WString::tr("Lms.Settings.demo-cannot-save")); return; From 24bf83d5403e32591a65e5b82f15f17315cbac75 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 13 Sep 2024 17:48:23 +0200 Subject: [PATCH 3/3] Fixed format --- src/lms/ui/LmsApplication.hpp | 4 ++-- src/lms/ui/explore/Filters.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lms/ui/LmsApplication.hpp b/src/lms/ui/LmsApplication.hpp index aeb3df94..4a63195a 100644 --- a/src/lms/ui/LmsApplication.hpp +++ b/src/lms/ui/LmsApplication.hpp @@ -66,8 +66,8 @@ namespace lms::ui db::ObjectPtr getUser(); db::UserId getUserId() const; - bool isUserAuthStrong() const; // user must be logged in prior this call - db::UserType getUserType() const; // user must be logged in prior this call + bool isUserAuthStrong() const; // user must be logged in prior this call + db::UserType getUserType() const; // user must be logged in prior this call std::string_view getUserLoginName() const; // user must be logged in prior this call // Proxified scanner events diff --git a/src/lms/ui/explore/Filters.cpp b/src/lms/ui/explore/Filters.cpp index 1529e47c..04cef313 100644 --- a/src/lms/ui/explore/Filters.cpp +++ b/src/lms/ui/explore/Filters.cpp @@ -55,7 +55,7 @@ namespace lms::ui auto transaction{ LmsApp->getDbSession().createReadTransaction() }; db::ClusterType::find(LmsApp->getDbSession(), [&](const db::ClusterType::pointer& clusterType) { typeModel->add(Wt::WString::fromUTF8(std::string{ clusterType->getName() }), clusterType->getId()); - }); + }); } typeModel->add(Wt::WString::tr("Lms.Explore.media-library"), MediaLibraryTag{}); @@ -78,7 +78,7 @@ namespace lms::ui { db::MediaLibrary::find(session, [&](const db::MediaLibrary::pointer& library) { valueModel->add(Wt::WString::fromUTF8(std::string{ library->getName() }), library->getId()); - }); + }); } else if (const db::ClusterTypeId * clusterTypeId{ std::get_if(&type) }) { @@ -88,7 +88,7 @@ namespace lms::ui db::Cluster::find(session, params, [&](const db::Cluster::pointer& cluster) { valueModel->add(Wt::WString::fromUTF8(std::string{ cluster->getName() }), cluster->getId()); - }); + }); } return valueModel; @@ -125,12 +125,12 @@ namespace lms::ui // TODO LmsApp->getModalManager().dispose(dialogPtr); - }); + }); Wt::WPushButton* cancelBtn{ dialog->bindNew("cancel-btn", Wt::WString::tr("Lms.cancel")) }; cancelBtn->clicked().connect([=] { LmsApp->getModalManager().dispose(dialogPtr); - }); + }); typeCombo->activated().connect([valueCombo, typeModel](int row) { const TypeVariant type{ typeModel->getValue(row) }; @@ -138,7 +138,7 @@ namespace lms::ui const std::shared_ptr valueModel{ createValueModel(type) }; valueCombo->clear(); valueCombo->setModel(valueModel); - }); + }); typeCombo->activated().emit(0); // force emit to refresh the type combo model @@ -181,7 +181,7 @@ namespace lms::ui _filters->removeWidget(filter); _clusterIds.erase(std::remove_if(std::begin(_clusterIds), std::end(_clusterIds), [clusterId](db::ClusterId id) { return id == clusterId; }), std::end(_clusterIds)); _sigUpdated.emit(); - }); + }); emitFilterAddedNotification(); } @@ -214,7 +214,7 @@ namespace lms::ui _mediaLibraryFilter = nullptr; _sigUpdated.emit(); state::writeValue("filters_media_library_id", std::nullopt); - }); + }); emitFilterAddedNotification(); }