File indexing completed on 2024-04-28 05:50:07
0001 /* 0002 * SPDX-License-Identifier: GPL-3.0-or-later 0003 * SPDX-FileCopyrightText: 2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> 0004 */ 0005 0006 #include "flows_p.h" 0007 #include "cli.h" 0008 #include "state_p.h" 0009 #include "vms.h" 0010 0011 #include "../logging_p.h" 0012 0013 #include <KLocalizedString> 0014 #include <QTimer> 0015 0016 KEYSMITH_LOGGER(logger, ".app.flows_p") 0017 0018 namespace app 0019 { 0020 accounts::AccountStorage * storageOf(Keysmith *app) 0021 { 0022 Q_ASSERT_X(app, Q_FUNC_INFO, "should have a Keysmith application instance"); 0023 0024 auto accounts = app->store().accounts(); 0025 0026 Q_ASSERT_X(accounts, Q_FUNC_INFO, "should have an AccountStorage instance"); 0027 return accounts; 0028 } 0029 0030 Navigation * navigationFor(Keysmith *app) 0031 { 0032 auto nav = app->navigation(); 0033 Q_ASSERT_X(nav, Q_FUNC_INFO, "should have a Navigation instance"); 0034 return nav; 0035 } 0036 0037 InitialFlow::InitialFlow(Keysmith *app) : 0038 QObject(app), m_started(false), m_uriToAdd(false), m_uriParsed(false), m_passwordPromptResolved(false), 0039 m_app(app), m_input(new model::AccountInput(this)), 0040 m_passwordRequest(new model::PasswordRequest(storageOf(app)->secret(), this)) 0041 { 0042 QObject::connect(m_passwordRequest, &model::PasswordRequest::passwordRequestChanged, 0043 this, &InitialFlow::resume); 0044 QObject::connect(m_passwordRequest, &model::PasswordRequest::passwordAccepted, this, &InitialFlow::resume); 0045 QObject::connect(accountListOf(m_app), &model::SimpleAccountListModel::loadedChanged, 0046 this, &InitialFlow::resume); 0047 } 0048 0049 void InitialFlow::run(const QCommandLineParser &parser) 0050 { 0051 flowStateOf(m_app)->setFlowRunning(true); 0052 overviewStateOf(m_app)->setActionsEnabled(false); 0053 m_started = true; 0054 0055 const auto argv = parser.positionalArguments(); 0056 if (argv.isEmpty()) { 0057 qCDebug(logger) << "No URIs to handle, moving on:" << this; 0058 QTimer::singleShot(0, this, &InitialFlow::resume); 0059 return; 0060 } 0061 0062 qCDebug(logger) << "Will first parse given URI(s):" << this; 0063 0064 m_uriToAdd = true; 0065 auto job = new CommandLineAccountJob(m_input); 0066 QObject::connect(job, &CommandLineAccountJob::newAccountProcessed, this, &InitialFlow::onNewAccountProcessed); 0067 QObject::connect(job, &CommandLineAccountJob::newAccountInvalid, this, &InitialFlow::onNewAccountInvalid); 0068 job->run(argv[0]); 0069 } 0070 0071 void InitialFlow::onNewAccountProcessed(void) 0072 { 0073 Q_ASSERT_X(m_started, Q_FUNC_INFO, "should have properly started the flow first"); 0074 0075 m_uriParsed = true; 0076 0077 auto vm = new AddAccountViewModel(m_input, accountListOf(m_app), true, false); 0078 QObject::connect(vm, &AddAccountViewModel::accepted, this, &InitialFlow::resume); 0079 QObject::connect(vm, &AddAccountViewModel::cancelled, this, &InitialFlow::onNewAccountRejected); 0080 navigationFor(m_app)->navigate(Navigation::Page::AddAccount, vm); 0081 } 0082 0083 void InitialFlow::onNewAccountAccepted(void) 0084 { 0085 Q_ASSERT_X(m_started, Q_FUNC_INFO, "should have properly started the flow first"); 0086 Q_ASSERT_X(m_uriToAdd && m_uriParsed, Q_FUNC_INFO, "should have parsed URIs first"); 0087 0088 accountListOf(m_app)->addAccount(m_input); 0089 m_uriToAdd = false; 0090 QTimer::singleShot(0, this, &InitialFlow::resume); 0091 } 0092 0093 void InitialFlow::onNewAccountRejected(void) 0094 { 0095 Q_ASSERT_X(m_uriToAdd && m_uriParsed, Q_FUNC_INFO, "should have parsed URIs first"); 0096 m_uriToAdd = false; 0097 QTimer::singleShot(0, this, &InitialFlow::resume); 0098 } 0099 0100 void InitialFlow::onNewAccountInvalid(void) 0101 { 0102 Q_ASSERT_X(m_started, Q_FUNC_INFO, "should have properly started the flow first"); 0103 0104 auto vm = new ErrorViewModel( 0105 i18nc("@title:window", "Invalid account"), 0106 i18nc("@info:label", "The account you are trying to add is invalid. You can either quit the app, or continue without adding the account."), 0107 true 0108 ); 0109 QObject::connect(vm, &ErrorViewModel::dismissed, this, &InitialFlow::onNewAccountRejected); 0110 navigationFor(m_app)->navigate(Navigation::Page::Error, vm); 0111 } 0112 0113 void InitialFlow::resume(void) 0114 { 0115 if (!m_started) { 0116 qCDebug(logger) << "Blocking progress: flow has not been started yet:" << this; 0117 return; 0118 } 0119 0120 if (m_uriToAdd && !m_uriParsed) { 0121 qCDebug(logger) << "Blocking progress: URI parsing has not completed yet:" << this; 0122 return; 0123 } 0124 0125 if (m_passwordRequest->keyAvailable()) { 0126 if (m_uriToAdd) { 0127 const auto accounts = accountListOf(m_app); 0128 if (!accounts->isAccountStillAvailable(m_input->name(), m_input->issuer())) { 0129 auto vm = new RenameAccountViewModel(m_input, accountListOf(m_app)); 0130 QObject::connect(vm, &RenameAccountViewModel::cancelled, this, &InitialFlow::onNewAccountRejected); 0131 QObject::connect(vm, &RenameAccountViewModel::accepted, this, &InitialFlow::onNewAccountAccepted); 0132 navigationFor(m_app)->navigate(Navigation::Page::RenameAccount, vm); 0133 return; 0134 } 0135 if (accounts->loaded()) { 0136 QTimer::singleShot(0, this, &InitialFlow::onNewAccountAccepted); 0137 return; 0138 } 0139 0140 qCDebug(logger) 0141 << "Blocking progress: accounts not fully loaded:" 0142 << "Waiting to see if new account remains available:" << this; 0143 return; 0144 } 0145 0146 auto vm = new AccountsOverviewViewModel(m_app); 0147 navigationFor(m_app)->navigate(Navigation::Page::AccountsOverview, vm); 0148 overviewStateOf(m_app)->setActionsEnabled(true); 0149 auto flows = flowStateOf(m_app); 0150 flows->setFlowRunning(false); 0151 flows->setInitialFlowDone(true); 0152 QTimer::singleShot(0, this, &QObject::deleteLater); 0153 return; 0154 } 0155 0156 if (!m_passwordPromptResolved) { 0157 if (m_passwordRequest->firstRun()) { 0158 m_passwordPromptResolved = true; 0159 auto vm = new SetupPasswordViewModel(m_passwordRequest); 0160 navigationFor(m_app)->navigate(Navigation::Page::SetupPassword, vm); 0161 return; 0162 } 0163 if (m_passwordRequest->previouslyDefined()) { 0164 m_passwordPromptResolved = true; 0165 auto vm = new UnlockAccountsViewModel(m_passwordRequest); 0166 navigationFor(m_app)->navigate(Navigation::Page::UnlockAccounts, vm); 0167 return; 0168 } 0169 qCDebug(logger) << "Blocking progress: password request has not yet been resolved:" << this; 0170 return; 0171 } 0172 0173 qCDebug(logger) << "Blocking progress: waiting for the next event:" << this; 0174 } 0175 0176 ManualAddAccountFlow::ManualAddAccountFlow(Keysmith *app) : 0177 QObject(app), m_app(app), m_input(new model::AccountInput(this)) 0178 { 0179 Q_ASSERT_X(app, Q_FUNC_INFO, "should have a Keysmith instance"); 0180 } 0181 0182 void ManualAddAccountFlow::run(void) 0183 { 0184 flowStateOf(m_app)->setFlowRunning(true); 0185 overviewStateOf(m_app)->setActionsEnabled(false); 0186 0187 auto vm = new AddAccountViewModel(m_input, accountListOf(m_app), false, true); 0188 QObject::connect(vm, &AddAccountViewModel::accepted, this, &ManualAddAccountFlow::onAccepted); 0189 QObject::connect(vm, &AddAccountViewModel::cancelled, this, &ManualAddAccountFlow::back); 0190 navigationFor(m_app)->push(Navigation::Page::AddAccount, vm); 0191 } 0192 0193 void ManualAddAccountFlow::onAccepted(void) 0194 { 0195 accountListOf(m_app)->addAccount(m_input); 0196 QTimer::singleShot(0, this, &ManualAddAccountFlow::back); 0197 } 0198 0199 void ManualAddAccountFlow::back(void) 0200 { 0201 auto vm = new AccountsOverviewViewModel(m_app); 0202 navigationFor(m_app)->navigate(Navigation::Page::AccountsOverview, vm); 0203 overviewStateOf(m_app)->setActionsEnabled(true); 0204 flowStateOf(m_app)->setFlowRunning(false); 0205 QTimer::singleShot(0, this, &QObject::deleteLater); 0206 } 0207 0208 ExternalCommandLineFlow::ExternalCommandLineFlow(Keysmith *app) : 0209 QObject(app), m_app(app), m_input(new model::AccountInput(this)) 0210 { 0211 Q_ASSERT_X(app, Q_FUNC_INFO, "should have a Keysmith instance"); 0212 } 0213 0214 void ExternalCommandLineFlow::run(const QCommandLineParser &parser) 0215 { 0216 const auto argv = parser.positionalArguments(); 0217 if (argv.isEmpty()) { 0218 qCDebug(logger) << "No URIs to handle, nothing to do for external commandline:" << this; 0219 QTimer::singleShot(0, this, &ExternalCommandLineFlow::deleteLater); 0220 return; 0221 } 0222 0223 flowStateOf(m_app)->setFlowRunning(true); 0224 overviewStateOf(m_app)->setActionsEnabled(false); 0225 qCDebug(logger) << "Will parse given URI(s) from external commandline:" << this; 0226 0227 auto job = new CommandLineAccountJob(m_input); 0228 QObject::connect(job, &CommandLineAccountJob::newAccountProcessed, 0229 this, &ExternalCommandLineFlow::onNewAccountProcessed); 0230 QObject::connect(job, &CommandLineAccountJob::newAccountInvalid, 0231 this, &ExternalCommandLineFlow::onNewAccountInvalid); 0232 job->run(argv[0]); 0233 } 0234 0235 void ExternalCommandLineFlow::onNewAccountProcessed(void) 0236 { 0237 auto vm = new AddAccountViewModel(m_input, accountListOf(m_app), false, true); 0238 QObject::connect(vm, &AddAccountViewModel::accepted, this, &ExternalCommandLineFlow::onAccepted); 0239 QObject::connect(vm, &AddAccountViewModel::cancelled, this, &ExternalCommandLineFlow::back); 0240 navigationFor(m_app)->push(Navigation::Page::AddAccount, vm); 0241 } 0242 0243 void ExternalCommandLineFlow::onNewAccountInvalid(void) 0244 { 0245 auto vm = new ErrorViewModel( 0246 i18nc("@title:window", "Invalid account"), 0247 i18nc("@info:label", "The account you are trying to add is invalid. Continue without adding the account."), 0248 false 0249 ); 0250 QObject::connect(vm, &ErrorViewModel::dismissed, this, &ExternalCommandLineFlow::back); 0251 navigationFor(m_app)->navigate(Navigation::Page::Error, vm); 0252 } 0253 0254 void ExternalCommandLineFlow::onAccepted(void) 0255 { 0256 accountListOf(m_app)->addAccount(m_input); 0257 QTimer::singleShot(0, this, &ExternalCommandLineFlow::back); 0258 } 0259 0260 void ExternalCommandLineFlow::back(void) 0261 { 0262 auto vm = new AccountsOverviewViewModel(m_app); 0263 navigationFor(m_app)->navigate(Navigation::Page::AccountsOverview, vm); 0264 overviewStateOf(m_app)->setActionsEnabled(true); 0265 flowStateOf(m_app)->setFlowRunning(false); 0266 QTimer::singleShot(0, this, &QObject::deleteLater); 0267 } 0268 }