keysmith/src/app/flows_p.cpp

269 lines
11 KiB
C++

/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
*/
#include "flows_p.h"
#include "cli.h"
#include "state_p.h"
#include "vms.h"
#include "../logging_p.h"
#include <KLocalizedString>
#include <QTimer>
KEYSMITH_LOGGER(logger, ".app.flows_p")
namespace app
{
accounts::AccountStorage * storageOf(Keysmith *app)
{
Q_ASSERT_X(app, Q_FUNC_INFO, "should have a Keysmith application instance");
auto accounts = app->store().accounts();
Q_ASSERT_X(accounts, Q_FUNC_INFO, "should have an AccountStorage instance");
return accounts;
}
Navigation * navigationFor(Keysmith *app)
{
auto nav = app->navigation();
Q_ASSERT_X(nav, Q_FUNC_INFO, "should have a Navigation instance");
return nav;
}
InitialFlow::InitialFlow(Keysmith *app) :
QObject(app), m_started(false), m_uriToAdd(false), m_uriParsed(false), m_passwordPromptResolved(false),
m_app(app), m_input(new model::AccountInput(this)),
m_passwordRequest(new model::PasswordRequest(storageOf(app)->secret(), this))
{
QObject::connect(m_passwordRequest, &model::PasswordRequest::passwordRequestChanged,
this, &InitialFlow::resume);
QObject::connect(m_passwordRequest, &model::PasswordRequest::passwordAccepted, this, &InitialFlow::resume);
QObject::connect(accountListOf(m_app), &model::SimpleAccountListModel::loadedChanged,
this, &InitialFlow::resume);
}
void InitialFlow::run(const QCommandLineParser &parser)
{
flowStateOf(m_app)->setFlowRunning(true);
overviewStateOf(m_app)->setActionsEnabled(false);
m_started = true;
const auto argv = parser.positionalArguments();
if (argv.isEmpty()) {
qCDebug(logger) << "No URIs to handle, moving on:" << this;
QTimer::singleShot(0, this, &InitialFlow::resume);
return;
}
qCDebug(logger) << "Will first parse given URI(s):" << this;
m_uriToAdd = true;
auto job = new CommandLineAccountJob(m_input);
QObject::connect(job, &CommandLineAccountJob::newAccountProcessed, this, &InitialFlow::onNewAccountProcessed);
QObject::connect(job, &CommandLineAccountJob::newAccountInvalid, this, &InitialFlow::onNewAccountInvalid);
job->run(argv[0]);
}
void InitialFlow::onNewAccountProcessed(void)
{
Q_ASSERT_X(m_started, Q_FUNC_INFO, "should have properly started the flow first");
m_uriParsed = true;
auto vm = new AddAccountViewModel(m_input, accountListOf(m_app), true, false);
QObject::connect(vm, &AddAccountViewModel::accepted, this, &InitialFlow::resume);
QObject::connect(vm, &AddAccountViewModel::cancelled, this, &InitialFlow::onNewAccountRejected);
navigationFor(m_app)->navigate(Navigation::Page::AddAccount, vm);
}
void InitialFlow::onNewAccountAccepted(void)
{
Q_ASSERT_X(m_started, Q_FUNC_INFO, "should have properly started the flow first");
Q_ASSERT_X(m_uriToAdd && m_uriParsed, Q_FUNC_INFO, "should have parsed URIs first");
accountListOf(m_app)->addAccount(m_input);
m_uriToAdd = false;
QTimer::singleShot(0, this, &InitialFlow::resume);
}
void InitialFlow::onNewAccountRejected(void)
{
Q_ASSERT_X(m_uriToAdd && m_uriParsed, Q_FUNC_INFO, "should have parsed URIs first");
m_uriToAdd = false;
QTimer::singleShot(0, this, &InitialFlow::resume);
}
void InitialFlow::onNewAccountInvalid(void)
{
Q_ASSERT_X(m_started, Q_FUNC_INFO, "should have properly started the flow first");
auto vm = new ErrorViewModel(
i18nc("@title:window", "Invalid account"),
i18nc("@info:label", "The account you are trying to add is invalid. You can either quit the app, or continue without adding the account."),
true
);
QObject::connect(vm, &ErrorViewModel::dismissed, this, &InitialFlow::onNewAccountRejected);
navigationFor(m_app)->navigate(Navigation::Page::Error, vm);
}
void InitialFlow::resume(void)
{
if (!m_started) {
qCDebug(logger) << "Blocking progress: flow has not been started yet:" << this;
return;
}
if (m_uriToAdd && !m_uriParsed) {
qCDebug(logger) << "Blocking progress: URI parsing has not completed yet:" << this;
return;
}
if (m_passwordRequest->keyAvailable()) {
if (m_uriToAdd) {
const auto accounts = accountListOf(m_app);
if (!accounts->isAccountStillAvailable(m_input->name(), m_input->issuer())) {
auto vm = new RenameAccountViewModel(m_input, accountListOf(m_app));
QObject::connect(vm, &RenameAccountViewModel::cancelled, this, &InitialFlow::onNewAccountRejected);
QObject::connect(vm, &RenameAccountViewModel::accepted, this, &InitialFlow::onNewAccountAccepted);
navigationFor(m_app)->navigate(Navigation::Page::RenameAccount, vm);
return;
}
if (accounts->loaded()) {
QTimer::singleShot(0, this, &InitialFlow::onNewAccountAccepted);
return;
}
qCDebug(logger)
<< "Blocking progress: accounts not fully loaded:"
<< "Waiting to see if new account remains available:" << this;
return;
}
auto vm = new AccountsOverviewViewModel(m_app);
navigationFor(m_app)->navigate(Navigation::Page::AccountsOverview, vm);
overviewStateOf(m_app)->setActionsEnabled(true);
auto flows = flowStateOf(m_app);
flows->setFlowRunning(false);
flows->setInitialFlowDone(true);
QTimer::singleShot(0, this, &QObject::deleteLater);
return;
}
if (!m_passwordPromptResolved) {
if (m_passwordRequest->firstRun()) {
m_passwordPromptResolved = true;
auto vm = new SetupPasswordViewModel(m_passwordRequest);
navigationFor(m_app)->navigate(Navigation::Page::SetupPassword, vm);
return;
}
if (m_passwordRequest->previouslyDefined()) {
m_passwordPromptResolved = true;
auto vm = new UnlockAccountsViewModel(m_passwordRequest);
navigationFor(m_app)->navigate(Navigation::Page::UnlockAccounts, vm);
return;
}
qCDebug(logger) << "Blocking progress: password request has not yet been resolved:" << this;
return;
}
qCDebug(logger) << "Blocking progress: waiting for the next event:" << this;
}
ManualAddAccountFlow::ManualAddAccountFlow(Keysmith *app) :
QObject(app), m_app(app), m_input(new model::AccountInput(this))
{
Q_ASSERT_X(app, Q_FUNC_INFO, "should have a Keysmith instance");
}
void ManualAddAccountFlow::run(void)
{
flowStateOf(m_app)->setFlowRunning(true);
overviewStateOf(m_app)->setActionsEnabled(false);
auto vm = new AddAccountViewModel(m_input, accountListOf(m_app), false, true);
QObject::connect(vm, &AddAccountViewModel::accepted, this, &ManualAddAccountFlow::onAccepted);
QObject::connect(vm, &AddAccountViewModel::cancelled, this, &ManualAddAccountFlow::back);
navigationFor(m_app)->push(Navigation::Page::AddAccount, vm);
}
void ManualAddAccountFlow::onAccepted(void)
{
accountListOf(m_app)->addAccount(m_input);
QTimer::singleShot(0, this, &ManualAddAccountFlow::back);
}
void ManualAddAccountFlow::back(void)
{
auto vm = new AccountsOverviewViewModel(m_app);
navigationFor(m_app)->navigate(Navigation::Page::AccountsOverview, vm);
overviewStateOf(m_app)->setActionsEnabled(true);
flowStateOf(m_app)->setFlowRunning(false);
QTimer::singleShot(0, this, &QObject::deleteLater);
}
ExternalCommandLineFlow::ExternalCommandLineFlow(Keysmith *app) :
QObject(app), m_app(app), m_input(new model::AccountInput(this))
{
Q_ASSERT_X(app, Q_FUNC_INFO, "should have a Keysmith instance");
}
void ExternalCommandLineFlow::run(const QCommandLineParser &parser)
{
const auto argv = parser.positionalArguments();
if (argv.isEmpty()) {
qCDebug(logger) << "No URIs to handle, nothing to do for external commandline:" << this;
QTimer::singleShot(0, this, &ExternalCommandLineFlow::deleteLater);
return;
}
flowStateOf(m_app)->setFlowRunning(true);
overviewStateOf(m_app)->setActionsEnabled(false);
qCDebug(logger) << "Will parse given URI(s) from external commandline:" << this;
auto job = new CommandLineAccountJob(m_input);
QObject::connect(job, &CommandLineAccountJob::newAccountProcessed,
this, &ExternalCommandLineFlow::onNewAccountProcessed);
QObject::connect(job, &CommandLineAccountJob::newAccountInvalid,
this, &ExternalCommandLineFlow::onNewAccountInvalid);
job->run(argv[0]);
}
void ExternalCommandLineFlow::onNewAccountProcessed(void)
{
auto vm = new AddAccountViewModel(m_input, accountListOf(m_app), false, true);
QObject::connect(vm, &AddAccountViewModel::accepted, this, &ExternalCommandLineFlow::onAccepted);
QObject::connect(vm, &AddAccountViewModel::cancelled, this, &ExternalCommandLineFlow::back);
navigationFor(m_app)->push(Navigation::Page::AddAccount, vm);
}
void ExternalCommandLineFlow::onNewAccountInvalid(void)
{
auto vm = new ErrorViewModel(
i18nc("@title:window", "Invalid account"),
i18nc("@info:label", "The account you are trying to add is invalid. Continue without adding the account."),
false
);
QObject::connect(vm, &ErrorViewModel::dismissed, this, &ExternalCommandLineFlow::back);
navigationFor(m_app)->navigate(Navigation::Page::Error, vm);
}
void ExternalCommandLineFlow::onAccepted(void)
{
accountListOf(m_app)->addAccount(m_input);
QTimer::singleShot(0, this, &ExternalCommandLineFlow::back);
}
void ExternalCommandLineFlow::back(void)
{
auto vm = new AccountsOverviewViewModel(m_app);
navigationFor(m_app)->navigate(Navigation::Page::AccountsOverview, vm);
overviewStateOf(m_app)->setActionsEnabled(true);
flowStateOf(m_app)->setFlowRunning(false);
QTimer::singleShot(0, this, &QObject::deleteLater);
}
}