diff --git a/CMakeLists.txt b/CMakeLists.txt index 6655cd8..8d9c235 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,10 +68,6 @@ set_target_properties(beerlog PROPERTIES WIN32_EXECUTABLE TRUE ) -#install(TARGETS beerlog -# BUNDLE DESTINATION . -# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) - if(QT_VERSION_MAJOR EQUAL 6) qt_import_qml_plugins(beerlog) qt_finalize_executable(beerlog) diff --git a/beerlog_ru_RU.ts b/beerlog_ru_RU.ts index 4c056ff..2dd95d2 100644 --- a/beerlog_ru_RU.ts +++ b/beerlog_ru_RU.ts @@ -4,9 +4,32 @@ MainMenu - - BeerLog v0.1 - + + Online + В сети + + + + Offline + Не в сети + + + + SettingsView + + + Settings + Настройки + + + + BeerLog service address + Адрес сервиса BeerLog + + + + Selected user id + Выбранный пользователь @@ -17,24 +40,24 @@ - + BeerLog v0.1 BeerLog v1.0.0 - + Settings Настройки - - + + Quit Выход - + Realy quit the application? Действительно выйти из приложения? diff --git a/main.cpp b/main.cpp index 0cc090e..d0be815 100644 --- a/main.cpp +++ b/main.cpp @@ -7,6 +7,7 @@ #include "viewmodels/usersviewmodel.h" #include "services/beerservice.h" +#include "services/settingsservice.h" int main(int argc, char *argv[]) { @@ -16,6 +17,11 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); QTranslator translator; + + if (!translator.load("qt_ru.qm", "qrc:/")) { + qWarning() << "Cant load"; + } + const QStringList uiLanguages = QLocale::system().uiLanguages(); for (const QString &locale : uiLanguages) { const QString baseName = "beerlog_" + QLocale(locale).name(); @@ -33,7 +39,11 @@ int main(int argc, char *argv[]) QCoreApplication::exit(-1); }, Qt::QueuedConnection); + engine.addImportPath("qrc:/"); + engine.rootContext()->setContextProperty("beerService", BeerService::instance()); + engine.rootContext()->setContextProperty("settingsService", SettingsService::instance()); + qmlRegisterType("ru.ded.beerlog", 1, 0, "UsersViewModel"); engine.load(url); diff --git a/models/usersmodel.h b/models/usersmodel.h index 728c333..382de44 100644 --- a/models/usersmodel.h +++ b/models/usersmodel.h @@ -4,7 +4,6 @@ #include #include -#include "services/settingsservice.h" #include "models/abstractmodel.h" class UsersModel : public AbstractModel diff --git a/qml/Components/MainMenu.qml b/qml/Components/MainMenu.qml index b962e26..5346d01 100644 --- a/qml/Components/MainMenu.qml +++ b/qml/Components/MainMenu.qml @@ -5,6 +5,7 @@ Drawer { property alias logo: logoImage.source property alias appName: appNameLabel.text property alias model: menuRepeater.model + property alias connected: connectionLabel.connected signal actionSelected(var action) @@ -26,12 +27,23 @@ Drawer { anchors.margins: 10 } - Label { - id: appNameLabel - + Column { anchors.verticalCenter: parent.verticalCenter - font.pointSize: 20 - text: qsTr("BeerLog v0.1") + + Label { + id: appNameLabel + + font.pointSize: 20 + } + + Label { + id: connectionLabel + + property bool connected: false + + text: connected ? qsTr("Online") : qsTr("Offline") + color: connected ? "green" : "red" + } } } diff --git a/qml/Components/SubtitledItemDelegate.qml b/qml/Components/SubtitledItemDelegate.qml new file mode 100644 index 0000000..df5d8d0 --- /dev/null +++ b/qml/Components/SubtitledItemDelegate.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 +import QtQuick.Controls 2.2 + +ItemDelegate { + id: root + + property string subtitle: "" + + contentItem: Column { + Label { + id: titleLabel + + width: parent.width + text: root.text + } + + Label { + width: parent.width + font.pixelSize: titleLabel.font.pixelSize - 2 + text: root.subtitle + opacity: 0.8 + } + } +} diff --git a/qml/Components/qmldir b/qml/Components/qmldir new file mode 100644 index 0000000..5c956d0 --- /dev/null +++ b/qml/Components/qmldir @@ -0,0 +1,5 @@ +module Components + +MainMenu 1.0 MainMenu.qml +MenuBackButton 1.0 MenuBackButton.qml +SubtitledItemDelegate 1.0 SubtitledItemDelegate.qml diff --git a/qml/Views/SettingsView.qml b/qml/Views/SettingsView.qml index ed62c36..ff23454 100644 --- a/qml/Views/SettingsView.qml +++ b/qml/Views/SettingsView.qml @@ -1,11 +1,74 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 +import Components 1.0 Page { + id: root - Label { - anchors.centerIn: parent - text: "Settings" + title: qsTr("Settings") + + ListModel { + id: settingsModel + + ListElement { + title: qsTr("BeerLog service address") + name: "serverAddress" + } + + ListElement { + title: qsTr("Selected user id") + name: "selectedUserId" + } } + ListView { + anchors.fill: parent + + model: settingsModel + + delegate: SubtitledItemDelegate { + width: parent.width + + text: model.title + subtitle: settingsService[model.name] + + onClicked: { + inputDialog.title = model.title + inputDialog.name = model.name + inputDialog.open() + } + } + } + + Dialog { + id: inputDialog + + property string name: "" + + width: root.width * 0.8 + + anchors.centerIn: parent + parent: ApplicationWindow.overlay + + modal: true + standardButtons: Dialog.Ok | Dialog.Cancel + + Column { + spacing: 20 + anchors.fill: parent + + TextField { + id: textField + + width: parent.width + inputMethodHints: Qt.ImhNoAutoUppercase + placeholderText: inputDialog.title + text: settingsService[inputDialog.name] || "" + } + } + + onAccepted: { + settingsService[inputDialog.name] = textField.text + } + } } diff --git a/qml/main.qml b/qml/main.qml index 5e094a7..a273d35 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -5,7 +5,7 @@ import QtQuick.Layouts 1.15 import QtWebSockets import ru.ded.beerlog 1.0 -import "Components" +import Components 1.0 ApplicationWindow { width: 640 @@ -16,15 +16,23 @@ ApplicationWindow { header: ToolBar { RowLayout { anchors.fill: parent + MenuBackButton { state: stackView.depth > 1 ? "back" : "menu" onClicked: mainMenu.open() onBack: stackView.pop() } - ToolButton { - text: usersModel.selectedUserName + + Label { + text: stackView.currentItem.title || usersModel.selectedUserName Layout.fillWidth: true - onClicked: usersMenu.open() + horizontalAlignment: Qt.AlignCenter + + MouseArea { + anchors.fill: parent + enabled: stackView.depth === 1 + onClicked: usersMenu.open() + } } } @@ -62,6 +70,7 @@ ApplicationWindow { logo: "qrc:/logo.png" appName: qsTr("BeerLog v0.1") + connected: beerService.connected model: ListModel { ListElement { diff --git a/qml/qml.qrc b/qml/qml.qrc index c5d13fd..ca1e243 100644 --- a/qml/qml.qrc +++ b/qml/qml.qrc @@ -8,5 +8,8 @@ Components/MainMenu.qml Views/OrdersView.qml Views/SettingsView.qml + Components/SubtitledItemDelegate.qml + Components/qmldir + qt_ru.qm diff --git a/services/beerservice.cpp b/services/beerservice.cpp index 741e5bc..823fcbb 100644 --- a/services/beerservice.cpp +++ b/services/beerservice.cpp @@ -7,6 +7,8 @@ #include #include +#include "settingsservice.h" + BeerService::BeerService() : QObject{nullptr} { @@ -38,6 +40,13 @@ BeerService::BeerService() sendCommand(m_commandStash.takeFirst().toMap()); } }); + + connect(&m_socket, &QWebSocket::stateChanged, this, &BeerService::connectedChanged); + + connect(settings(), &SettingsService::serverAddressChanged, this, &BeerService::reconnect); + connect(settings(), &SettingsService::selectedUserIdChanged, this, &BeerService::reconnect); + + reconnect(); } BeerService::~BeerService() @@ -46,15 +55,9 @@ BeerService::~BeerService() m_socket.close(); } -void BeerService::connectSrv(const QString &userId) +SettingsService *BeerService::settings() const { - if (QAbstractSocket::ConnectedState == m_socket.state()) { - m_socket.close(); - } - - QNetworkRequest request(QUrl("ws://195.133.196.161:8000")); - request.setRawHeader("Authorization", "Basic " + QString("%1:pass").arg(userId).toLatin1().toBase64()); - m_socket.open(request); + return SettingsService::instance(); } void BeerService::sendCommand(const QString &entity, const QString &action, const QVariantMap &data) @@ -104,12 +107,32 @@ void BeerService::restoreStash() } } +void BeerService::reconnect() +{ + if (connected()) { + m_socket.close(); + } + + QString userId = settings()->selectedUserId(); + QUrl serverUrl(QString("ws://%1").arg(settings()->serverAddress())); + + QNetworkRequest request(serverUrl); + request.setRawHeader("Authorization", "Basic " + QString("%1:pass").arg(userId).toLatin1().toBase64()); + m_socket.open(request); + +} + void BeerService::sendCommand(const QVariantMap &command) { - if (QAbstractSocket::ConnectedState == m_socket.state()) { + if (connected()) { QJsonDocument doc = QJsonDocument::fromVariant(command); m_socket.sendTextMessage(doc.toJson(QJsonDocument::Compact)); } else { m_commandStash << command; } } + +bool BeerService::connected() const +{ + return QAbstractSocket::ConnectedState == m_socket.state(); +} diff --git a/services/beerservice.h b/services/beerservice.h index d18a712..6eb506f 100644 --- a/services/beerservice.h +++ b/services/beerservice.h @@ -4,10 +4,13 @@ #include #include +class SettingsService; class BeerService : public QObject { Q_OBJECT + Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) + public: static BeerService *instance() { @@ -15,19 +18,25 @@ public: return &i; } - Q_INVOKABLE void connectSrv(const QString &userId = QString()); - void sendCommand(const QString &entity, const QString &action, const QVariantMap &data = QVariantMap()); void connectListener(QObject *listener); +signals: + void connectedChanged(); + private: BeerService(); ~BeerService(); + SettingsService *settings() const; + QString stashFileName() const; void saveStash() const; void restoreStash(); + + void reconnect(); void sendCommand(const QVariantMap &command); + bool connected() const; QMultiMap m_listeners; diff --git a/services/settingsservice.cpp b/services/settingsservice.cpp index 194953f..f0ba617 100644 --- a/services/settingsservice.cpp +++ b/services/settingsservice.cpp @@ -3,6 +3,14 @@ namespace Defaults { constexpr auto GuestUserId = "2641ffe8cd4311eda27f0242ac120002"; +constexpr auto ServerAddress = "195.133.196.161:8000"; + +} + +namespace Keys { + +constexpr auto ServerAddress = "server_address"; +constexpr auto SelectedUser = "selected_user"; } @@ -16,12 +24,24 @@ void SettingsService::setValue(const QString &key, const QVariant &value) m_settings.setValue(key, value); } +QString SettingsService::serverAddress() const +{ + return m_settings.value(Keys::ServerAddress, Defaults::ServerAddress).toString(); +} + +void SettingsService::setServerAddress(const QString &address) +{ + m_settings.setValue(Keys::ServerAddress, address); + emit serverAddressChanged(); +} + QString SettingsService::selectedUserId() const { - return m_settings.value("selected_user", Defaults::GuestUserId).toString(); + return m_settings.value(Keys::SelectedUser, Defaults::GuestUserId).toString(); } void SettingsService::setSelectedUserId(const QString &userId) { - m_settings.setValue("selected_user", userId); + m_settings.setValue(Keys::SelectedUser, userId); + emit selectedUserIdChanged(); } diff --git a/services/settingsservice.h b/services/settingsservice.h index bbc3015..b0eac76 100644 --- a/services/settingsservice.h +++ b/services/settingsservice.h @@ -1,18 +1,40 @@ #ifndef SETTINGSSERVICE_H #define SETTINGSSERVICE_H +#include #include -class SettingsService +class SettingsService : public QObject { + Q_OBJECT + + Q_PROPERTY(QString serverAddress READ serverAddress WRITE setServerAddress NOTIFY serverAddressChanged) + Q_PROPERTY(QString selectedUserId READ selectedUserId WRITE setSelectedUserId NOTIFY selectedUserIdChanged) + public: + static SettingsService *instance() + { + static SettingsService i; + return &i; + } + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; void setValue(const QString &key, const QVariant &value); + QString serverAddress() const; + void setServerAddress(const QString &address); + QString selectedUserId() const; void setSelectedUserId(const QString &userId); +signals: + void serverAddressChanged(); + void selectedUserIdChanged(); + private: + SettingsService() = default; + ~SettingsService() = default; + QSettings m_settings = QSettings("DedSoft", "BeerLog"); }; diff --git a/viewmodels/usersviewmodel.cpp b/viewmodels/usersviewmodel.cpp index 0bb98ff..4eabb6a 100644 --- a/viewmodels/usersviewmodel.cpp +++ b/viewmodels/usersviewmodel.cpp @@ -1,18 +1,15 @@ #include "usersviewmodel.h" -#include "services/beerservice.h" +#include "services/settingsservice.h" UsersViewModel::UsersViewModel(QObject *parent) : QObject{parent} { - connect(this, &UsersViewModel::selectedUserChanged, this, [this]() { - BeerService::instance()->connectSrv(m_selectedUser); - }); - - setSelectedUser(m_settings.selectedUserId()); - connect(&m_usersModel, &AbstractModel::dataChanged, this, &UsersViewModel::usersChanged); connect(&m_usersModel, &AbstractModel::dataChanged, this, &UsersViewModel::selectedUserNameChanged); + + connect(settings(), &SettingsService::selectedUserIdChanged, this, &UsersViewModel::selectedUserChanged); + connect(settings(), &SettingsService::selectedUserIdChanged, this, &UsersViewModel::selectedUserNameChanged); } QVariantList UsersViewModel::users() const @@ -22,22 +19,24 @@ QVariantList UsersViewModel::users() const QString UsersViewModel::selectedUser() const { - return m_selectedUser; + return settings()->selectedUserId(); } void UsersViewModel::setSelectedUser(const QString &newSelectedUser) { - if (m_selectedUser == newSelectedUser) { + if (selectedUser() == newSelectedUser) { return; } - m_selectedUser = newSelectedUser; - m_settings.setSelectedUserId(m_selectedUser); - emit selectedUserChanged(); - emit selectedUserNameChanged(); + settings()->setSelectedUserId(newSelectedUser); } QString UsersViewModel::selectedUserName() const { - return m_usersModel.userName(m_selectedUser); + return m_usersModel.userName(selectedUser()); +} + +SettingsService *UsersViewModel::settings() const +{ + return SettingsService::instance(); } diff --git a/viewmodels/usersviewmodel.h b/viewmodels/usersviewmodel.h index 892807f..d6edded 100644 --- a/viewmodels/usersviewmodel.h +++ b/viewmodels/usersviewmodel.h @@ -5,6 +5,7 @@ #include "models/usersmodel.h" +class SettingsService; class UsersViewModel : public QObject { Q_OBJECT @@ -27,10 +28,9 @@ signals: void selectedUserNameChanged(); private: - QString m_selectedUser; + SettingsService *settings() const; UsersModel m_usersModel; - SettingsService m_settings; }; #endif // USERSVIEWMODEL_H