Denis V. Dedkov 2 anni fa
parent
commit
44dd3cdb58

+ 5 - 3
CMakeLists.txt

@@ -16,9 +16,11 @@ set(TS_FILES beerlog_ru_RU.ts)
 
 set(PROJECT_SOURCES
         main.cpp
-        qml.qrc
+        qml/qml.qrc
+        models/abstractmodel.h models/abstractmodel.cpp
         models/summarymodel.h models/summarymodel.cpp
         models/usersmodel.h models/usersmodel.cpp
+        viewmodels/usersviewmodel.h viewmodels/usersviewmodel.cpp
         services/beerservice.h services/beerservice.cpp
         services/settingsservice.h services/settingsservice.cpp
         ${TS_FILES}
@@ -32,8 +34,8 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
         ${PROJECT_SOURCES}
     )
 # Define target properties for Android with Qt 6 as:
-#    set_property(TARGET beerlog APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
-#                 ${CMAKE_CURRENT_SOURCE_DIR}/android)
+    set_property(TARGET beerlog APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
+                 ${CMAKE_CURRENT_SOURCE_DIR}/android)
 # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
 
     qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})

+ 2 - 2
android/AndroidManifest.xml

@@ -1,9 +1,9 @@
 <?xml version="1.0"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ru.ded.beerlog" android:installLocation="auto" android:versionCode="0.0.1" android:versionName="-- %%INSERT_VERSION_NAME%% --">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ru.ded.beerlog" android:installLocation="auto" android:versionCode="1" android:versionName="0.1">
     <!-- %%INSERT_PERMISSIONS -->
     <!-- %%INSERT_FEATURES -->
     <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
-    <application android:name="org.qtproject.qt.android.bindings.QtApplication" android:extractNativeLibs="true" android:hardwareAccelerated="true" android:label="BeerLog" android:requestLegacyExternalStorage="true" android:allowNativeHeapPointerTagging="false">
+    <application android:name="org.qtproject.qt.android.bindings.QtApplication" android:extractNativeLibs="true" android:hardwareAccelerated="true" android:label="BeerLog" android:requestLegacyExternalStorage="true" android:allowNativeHeapPointerTagging="false" android:icon="@drawable/icon">
         <activity android:name="org.qtproject.qt.android.bindings.QtActivity" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:label="BeerLog" android:launchMode="singleTop" android:screenOrientation="unspecified">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>

BIN
android/res/drawable-hdpi/icon.png


BIN
android/res/drawable-ldpi/icon.png


BIN
android/res/drawable-mdpi/icon.png


BIN
android/res/drawable-xhdpi/icon.png


BIN
android/res/drawable-xxhdpi/icon.png


BIN
android/res/drawable-xxxhdpi/icon.png


+ 11 - 17
beerlog_ru_RU.ts

@@ -4,31 +4,25 @@
 <context>
     <name>main</name>
     <message>
-        <location filename="main.qml" line="13"/>
+        <location filename="qml/main.qml" line="14"/>
         <source>Beer Log</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="main.qml" line="19"/>
-        <source>‹</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.qml" line="28"/>
-        <source>⋮</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <source>Summary: %1 р.</source>
-        <translation type="vanished">Итого: %1 р.</translation>
+        <location filename="qml/main.qml" line="77"/>
+        <source>BeerLog v0.1</source>
+        <oldsource>BeerLog v1.0.0</oldsource>
+        <translation></translation>
     </message>
     <message>
-        <source>Order</source>
-        <translation type="vanished">Заказать</translation>
+        <location filename="qml/main.qml" line="82"/>
+        <source>Settings</source>
+        <translation>Настройки</translation>
     </message>
     <message>
-        <source>Ordered %1 items. Amount: %2 р.</source>
-        <translation type="vanished">Заказано %1 позиций на сумму %2 р.</translation>
+        <location filename="qml/main.qml" line="88"/>
+        <source>Quit</source>
+        <translation>Выход</translation>
     </message>
 </context>
 </TS>

+ 3 - 5
main.cpp

@@ -5,8 +5,7 @@
 #include <QTranslator>
 #include <QQmlContext>
 
-#include "models/summarymodel.h"
-#include "models/usersmodel.h"
+#include "viewmodels/usersviewmodel.h"
 #include "services/beerservice.h"
 
 int main(int argc, char *argv[])
@@ -34,9 +33,8 @@ int main(int argc, char *argv[])
             QCoreApplication::exit(-1);
     }, Qt::QueuedConnection);
 
-    engine.rootContext()->setContextProperty("beerService", new BeerService(&engine));
-    qmlRegisterType<SummaryModel>("ru.ded.beerlog", 1, 0, "SummaryModel");
-    qmlRegisterType<UsersModel>("ru.ded.beerlog", 1, 0, "UsersModel");
+    engine.rootContext()->setContextProperty("beerService", BeerService::instance());
+    qmlRegisterType<UsersViewModel>("ru.ded.beerlog", 1, 0, "UsersViewModel");
 
     engine.load(url);
 

+ 0 - 63
main.qml

@@ -1,63 +0,0 @@
-import QtQuick 2.15
-import QtQuick.Window 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.15
-import QtWebSockets
-
-import ru.ded.beerlog 1.0
-
-ApplicationWindow {
-    width: 640
-    height: 480
-    visible: true
-    title: qsTr("Beer Log")
-
-    header: ToolBar {
-        RowLayout {
-            anchors.fill: parent
-            ToolButton {
-                text: qsTr("‹")
-                onClicked: stack.pop()
-            }
-            ToolButton {
-                text: usersModel.selectedUserName
-                Layout.fillWidth: true
-                onClicked: usersMenu.open()
-            }
-            ToolButton {
-                text: qsTr("⋮")
-                onClicked: menu.open()
-            }
-        }
-
-        Menu {
-            id: usersMenu
-
-            Repeater {
-                model: usersModel.users
-
-                MenuItem {
-                    text: modelData.name
-
-                    onClicked: {
-                        usersModel.selectedUser = modelData.id
-                    }
-                }
-            }
-        }
-    }
-
-    UsersModel {
-        id: usersModel
-
-        Component.onCompleted: {
-            beerService.connectSrv(selectedUser)
-            beerService.connectListener(usersModel)
-            beerService.sendCommand("users", "get")
-        }
-
-        onSelectedUserChanged: {
-            beerService.connectSrv(selectedUser)
-        }
-    }
-}

+ 41 - 0
models/abstractmodel.cpp

@@ -0,0 +1,41 @@
+#include "abstractmodel.h"
+
+#include "services/beerservice.h"
+
+AbstractModel::AbstractModel(QObject *parent)
+    : QObject{parent}
+{
+}
+
+void AbstractModel::created(const QVariant &data)
+{
+    modified(data);
+}
+
+void AbstractModel::modified(const QVariant &data)
+{
+    QVariantMap d = data.toMap();
+    m_data[d.value("id").toString()] = d;
+
+    emit dataChanged();
+}
+
+void AbstractModel::deleted(const QVariant &data)
+{
+    QString id = data.toString();
+    m_data.remove(id);
+
+    emit dataChanged();
+}
+
+void AbstractModel::received(const QVariant &data)
+{
+    m_data = data.toMap();
+
+    emit dataChanged();
+}
+
+BeerService *AbstractModel::service() const
+{
+    return BeerService::instance();
+}

+ 34 - 0
models/abstractmodel.h

@@ -0,0 +1,34 @@
+#ifndef ABSTRACTMODEL_H
+#define ABSTRACTMODEL_H
+
+#include <QObject>
+#include <QVariantMap>
+
+class BeerService;
+class AbstractModel : public QObject
+{
+    Q_OBJECT
+
+    Q_PROPERTY(QString entity READ entity CONSTANT)
+
+public:
+    explicit AbstractModel(QObject *parent = nullptr);
+
+    virtual QString entity() const = 0;
+
+public slots:
+    void created(const QVariant &data);
+    void modified(const QVariant &data);
+    void deleted(const QVariant &data);
+    void received(const QVariant &data);
+
+signals:
+    void dataChanged();
+
+protected:
+    BeerService *service() const;
+
+    QVariantMap m_data;
+};
+
+#endif // ABSTRACTMODEL_H

+ 15 - 53
models/usersmodel.cpp

@@ -1,80 +1,42 @@
 #include "usersmodel.h"
 
-UsersModel::UsersModel(QObject *parent)
-    : QObject{parent}
-{
-    setSelectedUser(m_settings.value("selected_user").toString());
-}
+#include "services/beerservice.h"
 
-void UsersModel::created(const QVariant &data)
-{
-    modified(data);
-}
+namespace Keys {
 
-void UsersModel::modified(const QVariant &data)
-{
-    QVariantMap user = data.toMap();
-    m_users[user.value("id").toString()] = user;
+constexpr auto Users = "users";
+constexpr auto Name = "name";
 
-    emit usersChanged();
-    emit selectedUserNameChanged();
 }
 
-void UsersModel::deleted(const QVariant &data)
+UsersModel::UsersModel(QObject *parent)
+    : AbstractModel{parent}
 {
-    QString userId = data.toString();
-    m_users.remove(userId);
-
-    emit usersChanged();
-    emit selectedUserNameChanged();
+    service()->connectListener(this);
+    service()->sendCommand(Keys::Users, "get");
 }
 
-void UsersModel::received(const QVariant &data)
+QString UsersModel::entity() const
 {
-    m_users = data.toMap();
-
-    emit usersChanged();
-    emit selectedUserNameChanged();
+    return Keys::Users;
 }
 
 void UsersModel::connected(const QVariant &data)
 {
-    qInfo() << data.toMap().value("name").toString() << "connected";
+    qInfo() << data.toMap().value(Keys::Name).toString() << "connected";
 }
 
 void UsersModel::disconnected(const QVariant &data)
 {
-    qInfo() << data.toMap().value("name").toString() << "disconnected";
-}
-
-QString UsersModel::entity() const
-{
-    return QStringLiteral("users");
+    qInfo() << data.toMap().value(Keys::Name).toString() << "disconnected";
 }
 
 QVariantList UsersModel::users() const
 {
-    return m_users.values();
-}
-
-QString UsersModel::selectedUser() const
-{
-    return m_selectedUser;
-}
-
-void UsersModel::setSelectedUser(const QString &newSelectedUser)
-{
-    if (m_selectedUser == newSelectedUser) {
-        return;
-    }
-
-    m_selectedUser = newSelectedUser;
-    m_settings.setValue("selected_user", m_selectedUser);
-    emit selectedUserChanged();
-    emit selectedUserNameChanged();
+    return m_data.values();
 }
 
-QString UsersModel::selectedUserName() const
+QString UsersModel::userName(const QString &userId) const
 {
-    return m_users.value(m_selectedUser).toMap().value("name").toString();
+    return m_data.value(userId).toMap().value(Keys::Name).toString();
 }

+ 5 - 25
models/usersmodel.h

@@ -5,43 +5,23 @@
 #include <QVariantMap>
 
 #include "services/settingsservice.h"
+#include "models/abstractmodel.h"
 
-class UsersModel : public QObject
+class UsersModel : public AbstractModel
 {
     Q_OBJECT
 
-    Q_PROPERTY(QString entity READ entity CONSTANT)
-    Q_PROPERTY(QVariantList users READ users NOTIFY usersChanged)
-    Q_PROPERTY(QString selectedUser READ selectedUser WRITE setSelectedUser NOTIFY selectedUserChanged)
-    Q_PROPERTY(QString selectedUserName READ selectedUserName NOTIFY selectedUserNameChanged)
-
 public:
     explicit UsersModel(QObject *parent = nullptr);
 
-    QString entity() const;
+    QString entity() const override;
+
     QVariantList users() const;
-    QString selectedUser() const;
-    void setSelectedUser(const QString &newSelectedUser);
-    QString selectedUserName() const;
+    QString userName(const QString &userId) const;
 
 public slots:
-    void created(const QVariant &data);
-    void modified(const QVariant &data);
-    void deleted(const QVariant &data);
-    void received(const QVariant &data);
     void connected(const QVariant &data);
     void disconnected(const QVariant &data);
-
-signals:
-    void usersChanged();
-    void selectedUserChanged();
-    void selectedUserNameChanged();
-
-private:
-    QVariantMap m_users;
-    QString m_selectedUser;
-
-    SettingsService m_settings;
 };
 
 #endif // USERSMODEL_H

+ 84 - 0
qml/Components/MenuBackButton.qml

@@ -0,0 +1,84 @@
+import QtQuick 2.15
+
+Item {
+    id: root
+
+    width: 40
+    height: 40
+
+    property double iconMarigns: 8
+    property double iconHeight: width - iconMarigns * 2
+    signal clicked()
+    signal back()
+
+    SystemPalette {
+        id: palette
+    }
+
+    MouseArea {
+        id: ma
+
+        anchors.fill: parent
+    }
+
+    Rectangle {
+        id: bar1
+        x: root.iconMarigns
+        y: root.iconMarigns + root.iconHeight / 6
+        width: root.iconHeight
+        height: root.iconHeight / 9
+        antialiasing: true
+
+        color: palette.button
+    }
+
+    Rectangle {
+        id: bar2
+        x: root.iconMarigns
+        y: root.iconMarigns + root.iconHeight / 2 - height / 2
+        width: root.iconHeight
+        height: root.iconHeight / 9
+        antialiasing: true
+
+        color: palette.button
+    }
+
+    Rectangle {
+        id: bar3
+        x: root.iconMarigns
+        y: root.iconMarigns + root.iconHeight / 2 + height * 2
+        width: root.iconHeight
+        height: root.iconHeight / 9
+        antialiasing: true
+
+        color: palette.button
+    }
+
+    property int animationDuration: 450
+
+    state: "menu"
+    states: [
+        State {
+            name: "menu"
+            PropertyChanges { target: ma; onClicked: root.clicked() }
+        },
+
+        State {
+            name: "back"
+            PropertyChanges { target: root; rotation: 180 }
+            PropertyChanges { target: bar1; rotation: 45; width: root.iconHeight / 3 * 2; x: root.iconMarigns + root.iconHeight / 2; y: root.iconMarigns + root.iconHeight / 4 }
+            PropertyChanges { target: bar2; width: root.iconHeight / 6 * 5 + 1; x: root.iconMarigns + root.iconHeight / 9 }
+            PropertyChanges { target: bar3; rotation: -45; width: root.iconHeight / 3 * 2; x: root.iconMarigns + root.iconHeight / 2; y: root.iconMarigns + root.iconHeight / 3 * 2 }
+            PropertyChanges { target: ma; onClicked: root.back() }
+        }
+    ]
+
+    transitions: [
+        Transition {
+            RotationAnimation { target: root; direction: RotationAnimation.Clockwise; duration: animationDuration; easing.type: Easing.InOutQuad }
+            PropertyAnimation { target: bar1; properties: "rotation, width, x, y"; duration: animationDuration; easing.type: Easing.InOutQuad }
+            PropertyAnimation { target: bar2; properties: "rotation, width, x, y"; duration: animationDuration; easing.type: Easing.InOutQuad }
+            PropertyAnimation { target: bar3; properties: "rotation, width, x, y"; duration: animationDuration; easing.type: Easing.InOutQuad }
+        }
+    ]
+}

BIN
qml/logo.png


+ 94 - 0
qml/main.qml

@@ -0,0 +1,94 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+import QtWebSockets
+
+import ru.ded.beerlog 1.0
+import "Components"
+
+ApplicationWindow {
+    width: 640
+    height: 480
+    visible: true
+    title: qsTr("Beer Log")
+
+    header: ToolBar {
+        RowLayout {
+            anchors.fill: parent
+            MenuBackButton {
+                state: "menu"//stackView.depth > 1 ? "back" : "menu"
+                onClicked: drawer.open()
+                onBack: {
+                    state = "menu"
+                }
+            }
+            ToolButton {
+                text: usersModel.selectedUserName
+                Layout.fillWidth: true
+                onClicked: usersMenu.open()
+            }
+        }
+
+        Menu {
+            id: usersMenu
+
+            UsersViewModel {
+                id: usersModel
+            }
+
+            Repeater {
+                model: usersModel.users
+
+                MenuItem {
+                    text: modelData.name
+
+                    onClicked: {
+                        usersModel.selectedUser = modelData.id
+                    }
+                }
+            }
+        }
+    }
+
+    Drawer {
+        id: drawer
+
+        width: parent.width * 0.66
+        height: parent.height
+
+        Column {
+            anchors.fill: parent
+
+            Row {
+                width: parent.width
+                height: 100
+
+                Image {
+                    anchors.top: parent.top
+                    anchors.bottom: parent.bottom
+                    anchors.margins: 10
+                    source: "logo.png"
+                }
+
+                Label {
+                    anchors.verticalCenter: parent.verticalCenter
+                    font.pointSize: 20
+                    text: qsTr("BeerLog v0.1")
+                }
+            }
+
+            ItemDelegate {
+                text: qsTr("Settings")
+                width: parent.width
+                onClicked: stackView.openPage("SettingsForm.qml")
+            }
+
+            ItemDelegate {
+                text: qsTr("Quit")
+                width: parent.width
+                onClicked: Qt.quit()
+            }
+        }
+    }
+}

+ 3 - 0
qml.qrc → qml/qml.qrc

@@ -1,6 +1,9 @@
 <RCC>
     <qresource prefix="/">
         <file>main.qml</file>
+        <file>Components/MenuBackButton.qml</file>
+        <file>qtquickcontrols2.conf</file>
+        <file>logo.png</file>
         <file>beerlog_ru_RU.qm</file>
     </qresource>
 </RCC>

+ 13 - 0
qml/qtquickcontrols2.conf

@@ -0,0 +1,13 @@
+; This file can be edited to change the style of the application
+; Read "Qt Quick Controls 2 Configuration File" for details:
+; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
+
+[Controls]
+Style=Material
+
+[Material]
+Theme=Dark
+Primary=BlueGrey
+Accent=Grey
+;Foreground=Brown
+;Background=Steel

+ 3 - 10
services/beerservice.cpp

@@ -7,14 +7,8 @@
 #include <QFile>
 #include <QDebug>
 
-namespace {
-
-constexpr auto GuestUserId = "2641ffe8cd4311eda27f0242ac120002";
-
-}
-
-BeerService::BeerService(QObject *parent)
-    : QObject{parent}
+BeerService::BeerService()
+    : QObject{nullptr}
 {
     restoreStash();
 
@@ -59,8 +53,7 @@ void BeerService::connectSrv(const QString &userId)
     }
 
     QNetworkRequest request(QUrl("ws://195.133.196.161:8000"));
-    QString authString = QString("%1:pass").arg(userId.isEmpty() ? GuestUserId : userId);
-    request.setRawHeader("Authorization", "Basic " + authString.toLatin1().toBase64());
+    request.setRawHeader("Authorization", "Basic " + QString("%1:pass").arg(userId).toLatin1().toBase64());
     m_socket.open(request);
 }
 

+ 11 - 4
services/beerservice.h

@@ -9,14 +9,21 @@ class BeerService : public QObject
     Q_OBJECT
 
 public:
-    explicit BeerService(QObject *parent = nullptr);
-    ~BeerService();
+    static BeerService *instance()
+    {
+        static BeerService i;
+        return &i;
+    }
 
     Q_INVOKABLE void connectSrv(const QString &userId = QString());
-    Q_INVOKABLE void sendCommand(const QString &entity, const QString &action, const QVariantMap &data = QVariantMap());
-    Q_INVOKABLE void connectListener(QObject *listener);
+
+    void sendCommand(const QString &entity, const QString &action, const QVariantMap &data = QVariantMap());
+    void connectListener(QObject *listener);
 
 private:
+    BeerService();
+    ~BeerService();
+
     QString stashFileName() const;
     void saveStash() const;
     void restoreStash();

+ 16 - 0
services/settingsservice.cpp

@@ -1,5 +1,11 @@
 #include "settingsservice.h"
 
+namespace Defaults {
+
+constexpr auto GuestUserId = "2641ffe8cd4311eda27f0242ac120002";
+
+}
+
 QVariant SettingsService::value(const QString &key, const QVariant &defaultValue) const
 {
     return m_settings.value(key, defaultValue);
@@ -9,3 +15,13 @@ void SettingsService::setValue(const QString &key, const QVariant &value)
 {
     m_settings.setValue(key, value);
 }
+
+QString SettingsService::selectedUserId() const
+{
+    return m_settings.value("selected_user", Defaults::GuestUserId).toString();
+}
+
+void SettingsService::setSelectedUserId(const QString &userId)
+{
+    m_settings.setValue("selected_user", userId);
+}

+ 3 - 0
services/settingsservice.h

@@ -9,6 +9,9 @@ public:
     QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
     void setValue(const QString &key, const QVariant &value);
 
+    QString selectedUserId() const;
+    void setSelectedUserId(const QString &userId);
+
 private:
     QSettings m_settings = QSettings("DedSoft", "BeerLog");
 };

+ 43 - 0
viewmodels/usersviewmodel.cpp

@@ -0,0 +1,43 @@
+#include "usersviewmodel.h"
+
+#include "services/beerservice.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);
+}
+
+QVariantList UsersViewModel::users() const
+{
+    return m_usersModel.users();
+}
+
+QString UsersViewModel::selectedUser() const
+{
+    return m_selectedUser;
+}
+
+void UsersViewModel::setSelectedUser(const QString &newSelectedUser)
+{
+    if (m_selectedUser == newSelectedUser) {
+        return;
+    }
+
+    m_selectedUser = newSelectedUser;
+    m_settings.setSelectedUserId(m_selectedUser);
+    emit selectedUserChanged();
+    emit selectedUserNameChanged();
+}
+
+QString UsersViewModel::selectedUserName() const
+{
+    return m_usersModel.userName(m_selectedUser);
+}

+ 36 - 0
viewmodels/usersviewmodel.h

@@ -0,0 +1,36 @@
+#ifndef USERSVIEWMODEL_H
+#define USERSVIEWMODEL_H
+
+#include <QObject>
+
+#include "models/usersmodel.h"
+
+class UsersViewModel : public QObject
+{
+    Q_OBJECT
+
+    Q_PROPERTY(QVariantList users READ users NOTIFY usersChanged)
+    Q_PROPERTY(QString selectedUser READ selectedUser WRITE setSelectedUser NOTIFY selectedUserChanged)
+    Q_PROPERTY(QString selectedUserName READ selectedUserName NOTIFY selectedUserNameChanged)
+
+public:
+    explicit UsersViewModel(QObject *parent = nullptr);
+
+    QVariantList users() const;
+    QString selectedUser() const;
+    void setSelectedUser(const QString &newSelectedUser);
+    QString selectedUserName() const;
+
+signals:
+    void usersChanged();
+    void selectedUserChanged();
+    void selectedUserNameChanged();
+
+private:
+    QString m_selectedUser;
+
+    UsersModel m_usersModel;
+    SettingsService m_settings;
+};
+
+#endif // USERSVIEWMODEL_H