Files
QtPotato/src/translate/apikeystore.cpp
T

163 lines
3.5 KiB
C++

#include "apikeystore.h"
#include <QCoroSignal>
#include <KLocalizedString>
#include <QPointer>
#include <qt6keychain/keychain.h>
#include <memory>
using namespace Qt::Literals::StringLiterals;
namespace
{
const QString Service = u"dev.trivk.qtpotato"_s;
const QString Key = u"translation-api-key"_s;
struct JobDeleter {
void operator()(QKeychain::Job *job) const
{
job->deleteLater();
}
};
template<typename T>
std::unique_ptr<T, JobDeleter> makeJob()
{
auto job = std::unique_ptr<T, JobDeleter>(new T(Service));
job->setAutoDelete(false);
job->setKey(Key);
return job;
}
}
ApiKeyStore::ApiKeyStore(QObject *parent)
: QObject(parent)
{
refresh();
}
bool ApiKeyStore::hasKey() const
{
return m_hasKey;
}
QString ApiKeyStore::error() const
{
return m_error;
}
bool ApiKeyStore::busy() const
{
return m_busy;
}
void ApiKeyStore::setKey(const QString &key)
{
if (m_busy) {
return;
}
m_pendingKey = key.trimmed();
m_retryStore = true;
store(m_pendingKey);
}
void ApiKeyStore::retry()
{
if (m_busy) {
return;
}
if (m_retryStore) {
store(m_pendingKey);
} else {
refresh();
}
}
QCoro::Task<ApiKeyStore::ReadResult> ApiKeyStore::readKey()
{
auto job = makeJob<QKeychain::ReadPasswordJob>();
job->start();
co_await qCoro(job.get(), &QKeychain::Job::finished);
if (job->error() == QKeychain::EntryNotFound) {
co_return ReadResult{};
}
if (job->error() != QKeychain::NoError) {
co_return ReadResult{{}, i18nc("@info", "Could not read the API key from the system wallet.\nMake sure KWallet or Secret Service is running, then retry.")};
}
co_return ReadResult{job->textData(), {}};
}
QCoro::Task<> ApiKeyStore::refresh()
{
setBusy(true);
QPointer<ApiKeyStore> self(this);
const ReadResult result = co_await readKey();
if (!self) {
co_return;
}
setHasKey(!result.key.isEmpty());
setError(result.error);
setBusy(false);
}
QCoro::Task<> ApiKeyStore::store(QString key)
{
setBusy(true);
QPointer<ApiKeyStore> self(this);
std::unique_ptr<QKeychain::Job, JobDeleter> job;
if (key.isEmpty()) {
job = makeJob<QKeychain::DeletePasswordJob>();
} else {
auto write = makeJob<QKeychain::WritePasswordJob>();
write->setTextData(key);
job.reset(write.release());
}
job->start();
co_await qCoro(job.get(), &QKeychain::Job::finished);
if (!self) {
co_return;
}
if (job->error() != QKeychain::NoError && job->error() != QKeychain::EntryNotFound) {
setError(key.isEmpty()
? i18nc("@info", "Could not remove the API key from the system wallet.\nMake sure KWallet or Secret Service is running, then retry.")
: i18nc("@info", "Could not store the API key in the system wallet.\nMake sure KWallet or Secret Service is running, then retry."));
setBusy(false);
co_return;
}
setError({});
setHasKey(!key.isEmpty());
m_retryStore = false;
m_pendingKey.clear();
setBusy(false);
}
void ApiKeyStore::setHasKey(bool hasKey)
{
if (hasKey != m_hasKey) {
m_hasKey = hasKey;
Q_EMIT hasKeyChanged();
}
}
void ApiKeyStore::setError(const QString &error)
{
if (error != m_error) {
m_error = error;
Q_EMIT errorChanged();
}
}
void ApiKeyStore::setBusy(bool busy)
{
if (busy != m_busy) {
m_busy = busy;
Q_EMIT busyChanged();
}
}