refactor: introduce store for application state

The 'Store' is a convenient wrapper for injecting a dependency on the
entire application state (root) so it can be re-used by independent
flows of C++ logic. It will be used both to provide data to the view
models for the QML UI, and as a way to record results of actions
dispatched from the UI.

With this change the following (additional) state is defined:

 - OverviewState, which will be used to track state that determines how
   the AccountsOverview QML UI should render and what features are
   enabled/disabled in it. This state may be manipulated as a a
   side-effect of various interactios with arbitrary pages.
 - FlowState, which will be used to track state about which control/
   logic flows have completed and whether or not something is currently
   running. This is will be used to avoid distracting the user from an
   active UX flow with random unrelated prompts.
master
Johan Ouwerkerk 2021-02-15 20:40:57 +01:00 committed by Bhushan Shah
parent 131e59aa3a
commit bdcdb85bb6
5 changed files with 178 additions and 22 deletions

View File

@ -6,6 +6,7 @@
set(keysmith_SRCS
keysmith.cpp
cli.cpp
state_p.cpp
)
add_library(keysmith_lib STATIC ${keysmith_SRCS})

View File

@ -5,6 +5,8 @@
#include "keysmith.h"
#include "../logging_p.h"
#include "state_p.h"
#include <QClipboard>
#include <QGuiApplication>
@ -52,16 +54,53 @@ namespace app
Q_EMIT pushed(route, modelToTransfer);
}
Keysmith::Keysmith(Navigation *navigation, QObject *parent): QObject(parent), m_navigation(navigation), m_storage(nullptr)
static accounts::AccountStorage * openStorage(void)
{
qCDebug(logger) << "Initialising Keysmith account storage...";
const accounts::SettingsProvider settings([](const accounts::PersistenceAction &action) -> void
{
QSettings data(QStringLiteral("org.kde.keysmith"), QStringLiteral("Keysmith"));
action(data);
});
return accounts::AccountStorage::open(settings);
}
Store::Store(void) :
m_flows(QSharedPointer<FlowState>(new FlowState(), &QObject::deleteLater)),
m_overview(QSharedPointer<OverviewState>(new OverviewState(), &QObject::deleteLater)),
m_accounts(QSharedPointer<accounts::AccountStorage>(openStorage(), &accounts::AccountStorage::dispose)),
m_accountList(QSharedPointer<model::SimpleAccountListModel>(nullptr, &QObject::deleteLater))
{
m_accountList.reset(new model::SimpleAccountListModel(m_accounts.data()));
}
FlowState * Store::flows(void) const
{
return m_flows.data();
}
OverviewState * Store::overview(void) const
{
return m_overview.data();
}
accounts::AccountStorage * Store::accounts(void) const
{
return m_accounts.data();
}
model::SimpleAccountListModel * Store::accountList(void) const
{
return m_accountList.data();
}
Keysmith::Keysmith(Navigation *navigation, QObject *parent): QObject(parent), m_store(), m_navigation(navigation)
{
}
Keysmith::~Keysmith()
{
qCDebug(logger) << "Tearing down Keysmith application; requesting disposal of account storage";
if (m_storage) {
m_storage->dispose();
}
qCDebug(logger) << "Tearing down Keysmith application; disposal of account storage should be requested";
}
Navigation * Keysmith::navigation(void) const
@ -69,6 +108,11 @@ namespace app
return m_navigation;
}
const Store & Keysmith::store(void) const
{
return m_store;
};
void Keysmith::copyToClipboard(const QString &text)
{
QClipboard * clipboard = QGuiApplication::clipboard();
@ -82,25 +126,11 @@ namespace app
model::SimpleAccountListModel * Keysmith::accountListModel(void)
{
return new model::SimpleAccountListModel(storage(), this);
return new model::SimpleAccountListModel(m_store.accounts(), this);
}
model::PasswordRequest * Keysmith::passwordRequest(void)
{
return new model::PasswordRequest(storage()->secret(), this);
}
accounts::AccountStorage * Keysmith::storage(void)
{
if (!m_storage) {
const accounts::SettingsProvider settings([](const accounts::PersistenceAction &action) -> void
{
QSettings data(QStringLiteral("org.kde.keysmith"), QStringLiteral("Keysmith"));
action(data);
});
m_storage = accounts::AccountStorage::open(settings);
}
return m_storage;
return new model::PasswordRequest(m_store.accounts()->secret(), this);
}
}

View File

@ -12,6 +12,7 @@
#include <QMetaEnum>
#include <QObject>
#include <QQmlEngine>
#include <QSharedPointer>
namespace app
{
@ -42,6 +43,24 @@ namespace app
QQmlEngine * const m_engine;
};
class OverviewState;
class FlowState;
class Store
{
public:
explicit Store(void);
accounts::AccountStorage * accounts(void) const;
model::SimpleAccountListModel * accountList(void) const;
OverviewState * overview(void) const;
FlowState * flows(void) const;
private:
QSharedPointer<FlowState> m_flows;
QSharedPointer<OverviewState> m_overview;
QSharedPointer<accounts::AccountStorage> m_accounts;
QSharedPointer<model::SimpleAccountListModel> m_accountList;
};
class Keysmith: public QObject
{
Q_OBJECT
@ -49,6 +68,7 @@ namespace app
public:
explicit Keysmith(Navigation * const navigation, QObject *parent = nullptr);
virtual ~Keysmith();
const Store & store(void) const;
Navigation * navigation(void) const;
Q_INVOKABLE void copyToClipboard(const QString &text);
Q_INVOKABLE model::SimpleAccountListModel * accountListModel(void);
@ -56,8 +76,8 @@ namespace app
private:
accounts::AccountStorage * storage(void);
private:
Store m_store;
Navigation * const m_navigation;
accounts::AccountStorage *m_storage;
};
}

59
src/app/state_p.cpp Normal file
View File

@ -0,0 +1,59 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
*/
#include "state_p.h"
namespace app
{
OverviewState::OverviewState(QObject *parent) :
QObject(parent), m_actionsEnabled(false)
{
}
bool OverviewState::actionsEnabled(void) const
{
return m_actionsEnabled;
}
void OverviewState::setActionsEnabled(bool enabled)
{
if (m_actionsEnabled != enabled) {
m_actionsEnabled = enabled;
Q_EMIT actionsEnabledChanged();
}
}
FlowState::FlowState(QObject *parent) :
QObject(parent), m_flowRunning(false), m_initialFlowDone(false)
{
}
bool FlowState::flowRunning(void) const
{
return m_flowRunning;
}
void FlowState::setFlowRunning(bool running)
{
if (m_flowRunning != running) {
m_flowRunning = running;
Q_EMIT flowRunningChanged();
}
}
bool FlowState::initialFlowDone(void) const
{
return m_initialFlowDone;
}
void FlowState::setInitialFlowDone(bool done)
{
if (m_initialFlowDone != done) {
m_initialFlowDone = done;
Q_EMIT initialFlowDoneChanged();
}
}
}

46
src/app/state_p.h Normal file
View File

@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
*/
#ifndef APP_STATE_P_H
#define APP_STATE_P_H
#include <QObject>
namespace app
{
class OverviewState : public QObject
{
Q_OBJECT
Q_PROPERTY(bool actionsEnabled READ actionsEnabled WRITE setActionsEnabled NOTIFY actionsEnabledChanged)
public:
explicit OverviewState(QObject *parent = nullptr);
bool actionsEnabled(void) const;
void setActionsEnabled(bool enabled);
Q_SIGNALS:
void actionsEnabledChanged(void);
private:
bool m_actionsEnabled;
};
class FlowState : public QObject
{
Q_OBJECT
Q_PROPERTY(bool initialFlowDone READ initialFlowDone WRITE setInitialFlowDone NOTIFY initialFlowDoneChanged)
Q_PROPERTY(bool flowRunning READ flowRunning WRITE setFlowRunning NOTIFY flowRunningChanged)
public:
explicit FlowState(QObject *parent = nullptr);
bool flowRunning(void) const;
void setFlowRunning(bool running);
bool initialFlowDone(void) const;
void setInitialFlowDone(bool done);
Q_SIGNALS:
void initialFlowDoneChanged(void);
void flowRunningChanged(void);
private:
bool m_flowRunning;
bool m_initialFlowDone;
};
}
#endif