diff --git a/src/constants.h b/src/constants.h
new file mode 100644
index 0000000..1e58ea0
--- /dev/null
+++ b/src/constants.h
@@ -0,0 +1,11 @@
+#ifndef SAILPUSH_CONSTANTS_H
+#define SAILPUSH_CONSTANTS_H
+
+// Shared path constants used across daemon components.
+// Both NotificationManager (writer) and DbusInterface (reader) use this
+// to construct the pending_open file path from their cachePath.
+namespace SailPushPaths {
+ inline constexpr const char *PENDING_OPEN = "/pending_open";
+}
+
+#endif // SAILPUSH_CONSTANTS_H
diff --git a/src/credentialstore.cpp b/src/credentialstore.cpp
index 5272831..c90bbe2 100644
--- a/src/credentialstore.cpp
+++ b/src/credentialstore.cpp
@@ -93,7 +93,7 @@ bool CredentialStore::checkSecretExists()
return gsr.result().errorCode() == Sailfish::Secrets::Result::NoError;
}
-bool CredentialStore::save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName)
+bool CredentialStore::save(const QString &secret, const QString &deviceId)
{
if (!m_manager.isInitialized()) {
m_lastError = LoadError::BackendUnavailable;
@@ -108,8 +108,6 @@ bool CredentialStore::save(const QString &secret, const QString &deviceId, const
QJsonObject obj;
obj.insert("secret", secret);
obj.insert("device_id", deviceId);
- obj.insert("user_key", userKey);
- obj.insert("device_name", deviceName);
QByteArray jsonData = QJsonDocument(obj).toJson(QJsonDocument::Compact);
// Delete existing secret first (save() must succeed even if secret already exists)
@@ -153,7 +151,7 @@ bool CredentialStore::save(const QString &secret, const QString &deviceId, const
return true;
}
-bool CredentialStore::load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName)
+bool CredentialStore::load(QString &secret, QString &deviceId)
{
m_lastError = LoadError::None;
@@ -206,8 +204,6 @@ bool CredentialStore::load(QString &secret, QString &deviceId, QString &userKey,
QJsonObject obj = doc.object();
secret = obj.value("secret").toString();
deviceId = obj.value("device_id").toString();
- userKey = obj.value("user_key").toString();
- deviceName = obj.value("device_name").toString();
if (secret.isEmpty() || deviceId.isEmpty()) {
qCWarning(lcCredentialStore) << "Stored credentials have empty required fields";
diff --git a/src/credentialstore.h b/src/credentialstore.h
index 8dc1a1b..bced791 100644
--- a/src/credentialstore.h
+++ b/src/credentialstore.h
@@ -12,8 +12,8 @@ class CredentialStore : public ICredentialStore {
explicit CredentialStore(const QString &dataPath, QObject *parent = nullptr);
~CredentialStore() override;
- bool save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) override;
- bool load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) override;
+ bool save(const QString &secret, const QString &deviceId) override;
+ bool load(QString &secret, QString &deviceId) override;
bool clear() override;
bool hasCredentials() const override;
LoadError lastError() const override { return m_lastError; }
diff --git a/src/daemon.cpp b/src/daemon.cpp
index 28b0da5..e45763b 100644
--- a/src/daemon.cpp
+++ b/src/daemon.cpp
@@ -12,6 +12,17 @@ Q_LOGGING_CATEGORY(lcDaemon, "com.zackslash.sailpush.daemon")
static const int WS_DISCONNECT_TIMEOUT_MS = 30000;
static const int DEFAULT_POLLING_INTERVAL_MS = 5 * 60 * 1000;
+static int pollingIntervalFromIndex(int index)
+{
+ switch (index) {
+ case 0: return 60 * 1000;
+ case 1: return 5 * 60 * 1000;
+ case 2: return 15 * 60 * 1000;
+ case 3: return 30 * 60 * 1000;
+ default: return DEFAULT_POLLING_INTERVAL_MS;
+ }
+}
+
QString Daemon::dataPath()
{
return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
@@ -112,8 +123,8 @@ void Daemon::start()
qCWarning(lcDaemon) << "Failed to register D-Bus service, continuing anyway";
}
- QString secret, deviceId, userKey, deviceName;
- if (m_credentials->load(secret, deviceId, userKey, deviceName)) {
+ QString secret, deviceId;
+ if (m_credentials->load(secret, deviceId)) {
m_secret = secret;
m_deviceId = deviceId;
m_credentialsLoaded = true;
@@ -141,14 +152,7 @@ void Daemon::loadSettings()
m_pollingEnabled = settings.value("pollingFallback", true).toBool();
int intervalIndex = settings.value("pollingIntervalIndex", 1).toInt();
-
- switch (intervalIndex) {
- case 0: m_pollingIntervalMs = 60 * 1000; break;
- case 1: m_pollingIntervalMs = 5 * 60 * 1000; break;
- case 2: m_pollingIntervalMs = 15 * 60 * 1000; break;
- case 3: m_pollingIntervalMs = 30 * 60 * 1000; break;
- default: m_pollingIntervalMs = DEFAULT_POLLING_INTERVAL_MS; break;
- }
+ m_pollingIntervalMs = pollingIntervalFromIndex(intervalIndex);
qCInfo(lcDaemon) << "Settings loaded: polling=" << m_pollingEnabled << "interval=" << m_pollingIntervalMs;
@@ -175,10 +179,6 @@ void Daemon::performSync()
m_cpuKeepalive->start();
}
m_client->downloadMessages(m_secret, m_deviceId);
-
- if (!m_startupSyncDone) {
- m_startupSyncDone = true;
- }
}
void Daemon::deleteMessagesUpTo(const QString &highestId)
@@ -208,14 +208,6 @@ void Daemon::publishNotificationForMessage(const Message &msg, bool isNew)
}
}
-void Daemon::handleEmergencyMessage(const Message &msg)
-{
- if (msg.isEmergency() && !msg.acked) {
- qCInfo(lcDaemon) << "Emergency message detected:" << msg.id;
- publishNotificationForMessage(msg, true);
- }
-}
-
void Daemon::onDeviceRegistered(const QString &deviceId)
{
qCInfo(lcDaemon) << "Device registered:" << deviceId;
@@ -240,7 +232,10 @@ void Daemon::onMessagesDownloaded(const QList &messages)
if (!m_store->containsMessage(msg.id)) {
m_store->addMessage(msg);
newMessages.append(msg);
- handleEmergencyMessage(msg);
+ if (msg.isEmergency() && !msg.acked) {
+ qCInfo(lcDaemon) << "Emergency message detected:" << msg.id;
+ publishNotificationForMessage(msg, true);
+ }
}
}
@@ -254,7 +249,10 @@ void Daemon::onMessagesDownloaded(const QList &messages)
if (!newMessages.isEmpty() && m_startupSyncDone) {
for (const Message &msg : newMessages) {
- if (m_systemNotifications) {
+ // Emergency messages already received a notification in the
+ // inline check above; skip the redundant notification here
+ // while still signalling the UI that a new message arrived.
+ if (m_systemNotifications && !msg.isEmergency()) {
publishNotificationForMessage(msg, true);
}
m_dbus->onMessageReceived(msg);
@@ -265,6 +263,7 @@ void Daemon::onMessagesDownloaded(const QList &messages)
qCInfo(lcDaemon) << "Connecting WebSocket after sync";
m_wsManager->connectToServer(m_deviceId, m_secret);
}
+ m_startupSyncDone = true;
updateDiagnostics();
}
@@ -300,14 +299,6 @@ void Daemon::onMessagesDownloadFailed(const QString &error)
void Daemon::onMessagesDeleted()
{
qCInfo(lcDaemon) << "Messages deleted from server";
- if (!m_pendingDeleteIds.isEmpty()) {
- for (const QString &id : m_pendingDeleteIds) {
- m_store->removeMessage(id);
- }
- m_store->save();
- m_dbus->notifyUnreadCountChanged();
- m_pendingDeleteIds.clear();
- }
}
void Daemon::onMessageDeleteFailed(const QString &error)
@@ -422,8 +413,8 @@ void Daemon::onDbusRequestSync()
void Daemon::onDbusRequestReloadCredentials()
{
qCInfo(lcDaemon) << "D-Bus: reloading credentials";
- QString secret, deviceId, userKey, deviceName;
- if (m_credentials->load(secret, deviceId, userKey, deviceName)) {
+ QString secret, deviceId;
+ if (m_credentials->load(secret, deviceId)) {
m_secret = secret;
m_deviceId = deviceId;
m_credentialsLoaded = true;
@@ -458,14 +449,7 @@ void Daemon::onDbusRequestUpdateSettings(const QVariantMap &settings)
m_pollingEnabled = settings.value("pollingFallback").toBool();
}
if (settings.contains("pollingIntervalIndex")) {
- int intervalIndex = settings.value("pollingIntervalIndex").toInt();
- switch (intervalIndex) {
- case 0: m_pollingIntervalMs = 60 * 1000; break;
- case 1: m_pollingIntervalMs = 5 * 60 * 1000; break;
- case 2: m_pollingIntervalMs = 15 * 60 * 1000; break;
- case 3: m_pollingIntervalMs = 30 * 60 * 1000; break;
- default: m_pollingIntervalMs = DEFAULT_POLLING_INTERVAL_MS; break;
- }
+ m_pollingIntervalMs = pollingIntervalFromIndex(settings.value("pollingIntervalIndex").toInt());
}
if (settings.contains("preventDeepSleep")) {
m_preventDeepSleep = settings.value("preventDeepSleep").toBool();
@@ -497,9 +481,9 @@ void Daemon::onDbusRequestAcknowledge(const QString &receipt)
void Daemon::onDbusRequestDeleteMessage(const QString &messageId)
{
qCInfo(lcDaemon) << "D-Bus: delete message:" << messageId;
- // Track for deletion after server confirms
- m_pendingDeleteIds.insert(messageId);
- m_client->deleteMessages(m_secret, m_deviceId, messageId);
+ m_store->removeMessage(messageId);
+ m_store->save();
+ m_dbus->notifyUnreadCountChanged();
}
void Daemon::onDbusRequestQuit()
@@ -508,49 +492,42 @@ void Daemon::onDbusRequestQuit()
QCoreApplication::quit();
}
-void Daemon::onDbusRequestOpenMessage(const QString &messageId)
+bool Daemon::isUiRunning()
{
- qCInfo(lcDaemon) << "D-Bus: open message requested:" << messageId;
-
- // Write pending file so the app can read it on startup
- m_notificationManager->writePendingOpenMessage(messageId);
-
- // Emit signal for warm-start case (UI already running and receives this)
- m_dbus->notifyOpenMessageRequested(messageId);
-
- // Check if the UI is already running by scanning /proc for another
- // sailpush process besides our own (the daemon).
- // If it is, the D-Bus signal above is sufficient — don't spawn a duplicate.
- // If it's not, launch via invoker for cold start.
- bool uiRunning = false;
- qint64 myPid = QCoreApplication::applicationPid();
+ const qint64 myPid = QCoreApplication::applicationPid();
QDir procDir("/proc");
for (const QString &entry : procDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
bool ok;
- qint64 pid = entry.toLongLong(&ok);
- if (!ok || pid == myPid) continue;
-
+ const qint64 pid = entry.toLongLong(&ok);
+ if (!ok || pid == myPid)
+ continue;
QFile cmdlineFile(QStringLiteral("/proc/%1/cmdline").arg(pid));
- if (cmdlineFile.open(QIODevice::ReadOnly)) {
- QByteArray cmdline = cmdlineFile.readAll();
- // Match UI process: starts with "sailpush" but is NOT a daemon instance
- if ((cmdline.startsWith("sailpush") || cmdline.startsWith("/usr/bin/sailpush"))
- && !cmdline.contains("--daemon")) {
- uiRunning = true;
- break;
- }
+ if (!cmdlineFile.open(QIODevice::ReadOnly))
+ continue;
+ const QByteArray cmdline = cmdlineFile.readAll();
+ if ((cmdline.startsWith("sailpush") || cmdline.startsWith("/usr/bin/sailpush"))
+ && !cmdline.contains("--daemon")) {
+ return true;
}
}
+ return false;
+}
+
+void Daemon::onDbusRequestOpenMessage(const QString &messageId)
+{
+ qCInfo(lcDaemon) << "D-Bus: open message requested:" << messageId;
+
+ m_notificationManager->writePendingOpenMessage(messageId);
+ m_dbus->notifyOpenMessageRequested(messageId);
- if (!uiRunning) {
- // Cold start: launch the app via invoker
+ if (isUiRunning()) {
+ qCInfo(lcDaemon) << "UI already running, relying on D-Bus signal";
+ } else {
QTimer::singleShot(0, this, [this]() {
qCInfo(lcDaemon) << "Activating app via invoker (cold start)";
QProcess::startDetached("/usr/bin/invoker",
QStringList() << "--type=silica-qt5" << "/usr/bin/sailpush");
});
- } else {
- qCInfo(lcDaemon) << "UI already running, relying on D-Bus signal";
}
}
@@ -579,7 +556,6 @@ void Daemon::updateDiagnostics()
diag["wsDisconnectTimerActive"] = m_wsDisconnectTimer->isActive();
diag["secretPresent"] = !m_secret.isEmpty();
diag["deviceIdPresent"] = !m_deviceId.isEmpty();
- diag["pendingDeletes"] = m_pendingDeleteIds.size();
if (!m_credentialError.isEmpty()) {
diag["credentialError"] = m_credentialError;
}
diff --git a/src/daemon.h b/src/daemon.h
index f28e1d5..d06cb6e 100644
--- a/src/daemon.h
+++ b/src/daemon.h
@@ -4,7 +4,6 @@
#include
#include
#include
-#include
#include
#include "sailpushclient.h"
#include "websocketmanager.h"
@@ -61,7 +60,7 @@ private slots:
void performSync();
void deleteMessagesUpTo(const QString &highestId);
void publishNotificationForMessage(const Message &msg, bool isNew);
- void handleEmergencyMessage(const Message &msg);
+ bool isUiRunning();
void updateDiagnostics();
SailPushClient *m_client;
@@ -79,7 +78,6 @@ private slots:
QString m_secret;
QString m_deviceId;
- QSet m_pendingDeleteIds;
bool m_startupSyncDone;
bool m_credentialsLoaded;
diff --git a/src/dbusinterface.cpp b/src/dbusinterface.cpp
index 62dc826..353be6a 100644
--- a/src/dbusinterface.cpp
+++ b/src/dbusinterface.cpp
@@ -1,4 +1,5 @@
#include "dbusinterface.h"
+#include "constants.h"
#include
#include
#include
@@ -148,7 +149,7 @@ void DbusInterface::OpenMessage(const QString &messageId)
QString DbusInterface::GetPendingOpenMessage()
{
- QString path = m_cachePath + "/pending_open";
+ QString path = m_cachePath + SailPushPaths::PENDING_OPEN;
QFile file(path);
if (file.open(QIODevice::ReadOnly)) {
QString messageId = QString::fromUtf8(file.readAll()).trimmed();
@@ -217,21 +218,31 @@ void DbusInterface::notifyUnreadCountChanged()
emit unreadCountChanged();
}
-void DbusInterface::refreshAutoStartCache()
+void DbusInterface::runSystemctl(const QStringList &args,
+ std::function callback)
{
QProcess *proc = new QProcess(this);
connect(proc, static_cast(&QProcess::finished),
- this, [this, proc](int exitCode, QProcess::ExitStatus) {
- Q_UNUSED(exitCode);
- m_autoStartEnabled = QString::fromUtf8(proc->readAllStandardOutput()).trimmed() == "enabled";
- qCInfo(lcDbus) << "AutoStart cache refreshed:" << m_autoStartEnabled;
+ this, [this, proc, callback](int exitCode, QProcess::ExitStatus) {
+ QString stdOut = QString::fromUtf8(proc->readAllStandardOutput()).trimmed();
+ QString stdErr = QString::fromUtf8(proc->readAllStandardError()).trimmed();
+ callback(exitCode, stdOut, stdErr);
proc->deleteLater();
});
connect(proc, &QProcess::errorOccurred, this, [this, proc](QProcess::ProcessError) {
- qCWarning(lcDbus) << "Failed to check autostart status:" << proc->errorString();
+ qCWarning(lcDbus) << "systemctl failed:" << proc->errorString();
proc->deleteLater();
});
- proc->start("/usr/bin/systemctl", {"--user", "is-enabled", "sailpush"});
+ proc->start("/usr/bin/systemctl", args);
+}
+
+void DbusInterface::refreshAutoStartCache()
+{
+ runSystemctl({"--user", "is-enabled", "sailpush"},
+ [this](int, const QString &stdOut, const QString &) {
+ m_autoStartEnabled = (stdOut == "enabled");
+ qCInfo(lcDbus) << "AutoStart cache refreshed:" << m_autoStartEnabled;
+ });
}
bool DbusInterface::GetAutoStartEnabled()
@@ -242,26 +253,17 @@ bool DbusInterface::GetAutoStartEnabled()
void DbusInterface::SetAutoStartEnabled(bool enabled)
{
qCInfo(lcDbus) << "Setting AutoStart to" << enabled;
- QProcess *proc = new QProcess(this);
- connect(proc, static_cast(&QProcess::finished),
- this, [this, proc, enabled](int exitCode, QProcess::ExitStatus) {
- QString stdOut = QString::fromUtf8(proc->readAllStandardOutput()).trimmed();
- QString stdErr = QString::fromUtf8(proc->readAllStandardError()).trimmed();
- if (!stdOut.isEmpty()) qCInfo(lcDbus) << "systemctl stdout:" << stdOut;
- if (!stdErr.isEmpty()) qCWarning(lcDbus) << "systemctl stderr:" << stdErr;
- if (exitCode != 0) {
- qCWarning(lcDbus) << "systemctl exit code:" << exitCode;
- } else {
- m_autoStartEnabled = enabled;
- qCInfo(lcDbus) << "AutoStart cache updated:" << m_autoStartEnabled;
- }
- proc->deleteLater();
- });
- connect(proc, &QProcess::errorOccurred, this, [this, proc](QProcess::ProcessError) {
- qCWarning(lcDbus) << "Failed to set autostart:" << proc->errorString();
- proc->deleteLater();
- });
- proc->start("/usr/bin/systemctl", {"--user", enabled ? "enable" : "disable", "sailpush"});
+ runSystemctl({"--user", enabled ? "enable" : "disable", "sailpush"},
+ [this, enabled](int exitCode, const QString &stdOut, const QString &stdErr) {
+ if (!stdOut.isEmpty()) qCInfo(lcDbus) << "systemctl stdout:" << stdOut;
+ if (!stdErr.isEmpty()) qCWarning(lcDbus) << "systemctl stderr:" << stdErr;
+ if (exitCode != 0) {
+ qCWarning(lcDbus) << "systemctl exit code:" << exitCode;
+ } else {
+ m_autoStartEnabled = enabled;
+ qCInfo(lcDbus) << "AutoStart cache updated:" << m_autoStartEnabled;
+ }
+ });
}
void DbusInterface::onMessageReceived(const Message &msg)
diff --git a/src/dbusinterface.h b/src/dbusinterface.h
index 7fbf167..ffbfd74 100644
--- a/src/dbusinterface.h
+++ b/src/dbusinterface.h
@@ -3,6 +3,7 @@
#include
#include
+#include
#include "message.h"
#include "messagestore.h"
#include "websocketmanager.h"
@@ -87,6 +88,8 @@ private slots:
QString wsStateToString(WebSocketManager::ConnectionState state) const;
void refreshAutoStartCache();
+ void runSystemctl(const QStringList &args,
+ std::function callback);
MessageStore *m_store;
WebSocketManager *m_wsManager;
diff --git a/src/filecredentialstore.cpp b/src/filecredentialstore.cpp
index 6e638cd..5eb69e7 100644
--- a/src/filecredentialstore.cpp
+++ b/src/filecredentialstore.cpp
@@ -28,14 +28,12 @@ FileCredentialStore::~FileCredentialStore()
m_cachedKey.fill(0);
}
-bool FileCredentialStore::save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName)
+bool FileCredentialStore::save(const QString &secret, const QString &deviceId)
{
QJsonObject obj;
obj.insert("version", 2);
obj.insert("secret", QString::fromUtf8(encrypt(secret.toUtf8())));
obj.insert("device_id", QString::fromUtf8(encrypt(deviceId.toUtf8())));
- obj.insert("user_key", QString::fromUtf8(encrypt(userKey.toUtf8())));
- obj.insert("device_name", QString::fromUtf8(encrypt(deviceName.toUtf8())));
QJsonDocument doc(obj);
QFile file(storagePath());
@@ -63,7 +61,7 @@ bool FileCredentialStore::save(const QString &secret, const QString &deviceId, c
return true;
}
-bool FileCredentialStore::load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName)
+bool FileCredentialStore::load(QString &secret, QString &deviceId)
{
m_lastError = LoadError::None;
@@ -112,8 +110,6 @@ bool FileCredentialStore::load(QString &secret, QString &deviceId, QString &user
secret = decryptField("secret");
deviceId = decryptField("device_id");
- userKey = decryptField("user_key");
- deviceName = decryptField("device_name");
if (!ok) {
qCWarning(lcFileCredentialStore) << "Failed to decrypt some credential fields";
diff --git a/src/filecredentialstore.h b/src/filecredentialstore.h
index 964997e..b5b68d5 100644
--- a/src/filecredentialstore.h
+++ b/src/filecredentialstore.h
@@ -10,8 +10,8 @@ class FileCredentialStore : public ICredentialStore {
explicit FileCredentialStore(const QString &dataPath, QObject *parent = nullptr);
~FileCredentialStore() override;
- bool save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) override;
- bool load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) override;
+ bool save(const QString &secret, const QString &deviceId) override;
+ bool load(QString &secret, QString &deviceId) override;
bool clear() override;
bool hasCredentials() const override;
LoadError lastError() const override { return m_lastError; }
diff --git a/src/icredentialstore.h b/src/icredentialstore.h
index 2059d8d..14ea0b9 100644
--- a/src/icredentialstore.h
+++ b/src/icredentialstore.h
@@ -18,8 +18,8 @@ class ICredentialStore : public QObject {
explicit ICredentialStore(QObject *parent = nullptr) : QObject(parent) {}
virtual ~ICredentialStore() = default;
- virtual bool save(const QString &secret, const QString &deviceId, const QString &userKey, const QString &deviceName) = 0;
- virtual bool load(QString &secret, QString &deviceId, QString &userKey, QString &deviceName) = 0;
+ virtual bool save(const QString &secret, const QString &deviceId) = 0;
+ virtual bool load(QString &secret, QString &deviceId) = 0;
virtual bool clear() = 0;
virtual bool hasCredentials() const = 0;
virtual LoadError lastError() const = 0;
diff --git a/src/loginhelper.cpp b/src/loginhelper.cpp
index 136e573..0747eba 100644
--- a/src/loginhelper.cpp
+++ b/src/loginhelper.cpp
@@ -18,8 +18,8 @@ LoginHelper::LoginHelper(QObject *parent)
, m_registering(false)
, m_needsTwoFactor(false)
{
- QString secret, deviceId, userKey, deviceName;
- if (!m_store->load(secret, deviceId, userKey, deviceName)) {
+ QString secret, deviceId;
+ if (!m_store->load(secret, deviceId)) {
ICredentialStore::LoadError err = m_store->lastError();
if (err == ICredentialStore::LoadError::InvalidFormat) {
m_migrationReason = tr("App was upgraded — saved credentials use an older format and cannot be migrated. Please log in again.");
@@ -54,9 +54,7 @@ void LoginHelper::cancel()
setNeedsTwoFactor(false);
setErrorString(QString());
- m_pendingUserKey.clear();
m_pendingSecret.clear();
- m_pendingDeviceName.clear();
}
void LoginHelper::logout()
@@ -77,13 +75,12 @@ void LoginHelper::setMigrationReason(const QString &reason)
void LoginHelper::onLoginSuccess(const QString &userKey, const QString &secret)
{
qCInfo(lcLoginHelper) << "Login successful, registering device";
- m_pendingUserKey = userKey;
+ Q_UNUSED(userKey)
m_pendingSecret = secret;
setLoggingIn(false);
setRegistering(true);
- m_pendingDeviceName = generateDeviceName();
- m_client->registerDevice(secret, m_pendingDeviceName);
+ m_client->registerDevice(secret, generateDeviceName());
}
void LoginHelper::onLoginFailed(const QString &error)
@@ -107,7 +104,7 @@ void LoginHelper::onDeviceRegistered(const QString &deviceId)
qCInfo(lcLoginHelper) << "Device registered:" << deviceId;
setRegistering(false);
- bool saved = m_store->save(m_pendingSecret, deviceId, m_pendingUserKey, m_pendingDeviceName);
+ bool saved = m_store->save(m_pendingSecret, deviceId);
if (saved) {
qCInfo(lcLoginHelper) << "Credentials saved successfully";
if (!m_migrationReason.isEmpty()) {
@@ -121,9 +118,7 @@ void LoginHelper::onDeviceRegistered(const QString &deviceId)
emit loginFailed(tr("Failed to save credentials"));
}
- m_pendingUserKey.clear();
m_pendingSecret.clear();
- m_pendingDeviceName.clear();
}
void LoginHelper::onDeviceRegistrationFailed(const QString &error)
@@ -133,9 +128,7 @@ void LoginHelper::onDeviceRegistrationFailed(const QString &error)
setErrorString(error);
emit loginFailed(error);
- m_pendingUserKey.clear();
m_pendingSecret.clear();
- m_pendingDeviceName.clear();
}
void LoginHelper::setLoggingIn(bool value)
diff --git a/src/loginhelper.h b/src/loginhelper.h
index b97ed45..5823135 100644
--- a/src/loginhelper.h
+++ b/src/loginhelper.h
@@ -61,9 +61,7 @@ private slots:
bool m_needsTwoFactor;
QString m_errorString;
QString m_migrationReason;
- QString m_pendingUserKey;
QString m_pendingSecret;
- QString m_pendingDeviceName;
};
#endif // LOGINHELPER_H
diff --git a/src/main.cpp b/src/main.cpp
index 3e9c584..91701ef 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -27,19 +27,20 @@ static const QString DBUS_SERVICE = "com.zackslash.sailpush";
static void loadTranslations(QCoreApplication *app)
{
QLocale locale = QLocale::system();
+ const QString localeName = locale.name();
auto *translator = new QTranslator(app);
QString transDir = SailfishApp::pathTo("translations").toLocalFile();
- if (translator->load(locale.name(), "sailpush", "_", transDir)) {
+ if (translator->load(localeName, "sailpush", "_", transDir)) {
app->installTranslator(translator);
- qCInfo(lcMain) << "Loaded translations for" << locale.name();
+ qCInfo(lcMain) << "Loaded translations for" << localeName;
} else {
// Try language-only (e.g. "de" from "de_DE")
- QString lang = locale.name().left(locale.name().indexOf('_'));
+ QString lang = localeName.left(localeName.indexOf('_'));
if (!lang.isEmpty() && translator->load(lang, "sailpush", "_", transDir)) {
app->installTranslator(translator);
qCInfo(lcMain) << "Loaded translations for" << lang;
} else {
- qCInfo(lcMain) << "No translations found for" << locale.name() << "- using English";
+ qCInfo(lcMain) << "No translations found for" << localeName << "- using English";
delete translator;
}
}
diff --git a/src/messagestore.cpp b/src/messagestore.cpp
index f6b4be8..21ea3e1 100644
--- a/src/messagestore.cpp
+++ b/src/messagestore.cpp
@@ -41,10 +41,14 @@ bool MessageStore::load()
}
m_messages.clear();
+ m_idSet.clear();
QJsonArray array = doc.array();
for (const QJsonValue &v : array) {
m_messages.append(Message::fromJson(v.toObject()));
}
+ for (const Message &msg : m_messages) {
+ m_idSet.insert(msg.id);
+ }
qCInfo(lcMessageStore) << "Loaded" << m_messages.size() << "messages from store";
return true;
@@ -80,17 +84,6 @@ bool MessageStore::save() const
return true;
}
-QList MessageStore::unreadMessages() const
-{
- QList unread;
- for (const Message &msg : m_messages) {
- if (!msg.read) {
- unread.append(msg);
- }
- }
- return unread;
-}
-
int MessageStore::unreadCount() const
{
int count = 0;
@@ -104,20 +97,22 @@ int MessageStore::unreadCount() const
void MessageStore::addMessage(const Message &msg)
{
- if (containsMessage(msg.id)) {
+ if (m_idSet.contains(msg.id)) {
return;
}
m_messages.prepend(msg);
+ m_idSet.insert(msg.id);
trimMessages();
}
void MessageStore::markAsRead(const QString &id)
{
+ if (!m_idSet.contains(id)) {
+ return;
+ }
for (int i = 0; i < m_messages.size(); ++i) {
if (m_messages[i].id == id) {
- Message msg = m_messages[i];
- msg.read = true;
- m_messages[i] = msg;
+ m_messages[i].read = true;
break;
}
}
@@ -126,43 +121,33 @@ void MessageStore::markAsRead(const QString &id)
void MessageStore::markAllAsRead()
{
for (int i = 0; i < m_messages.size(); ++i) {
- Message msg = m_messages[i];
- msg.read = true;
- m_messages[i] = msg;
+ m_messages[i].read = true;
}
}
void MessageStore::removeMessage(const QString &id)
{
+ if (!m_idSet.contains(id)) {
+ return;
+ }
for (int i = 0; i < m_messages.size(); ++i) {
if (m_messages[i].id == id) {
m_messages.removeAt(i);
break;
}
}
-}
-
-QString MessageStore::highestMessageId() const
-{
- if (m_messages.isEmpty()) {
- return QString();
- }
- return m_messages.first().id;
+ m_idSet.remove(id);
}
bool MessageStore::containsMessage(const QString &id) const
{
- for (const Message &msg : m_messages) {
- if (msg.id == id) {
- return true;
- }
- }
- return false;
+ return m_idSet.contains(id);
}
void MessageStore::trimMessages()
{
while (m_messages.size() > m_maxMessages) {
+ m_idSet.remove(m_messages.last().id);
m_messages.removeLast();
}
}
diff --git a/src/messagestore.h b/src/messagestore.h
index 8c2ca1b..b2d7787 100644
--- a/src/messagestore.h
+++ b/src/messagestore.h
@@ -3,6 +3,7 @@
#include
#include
+#include
#include "message.h"
class MessageStore : public QObject {
@@ -17,7 +18,6 @@ class MessageStore : public QObject {
bool save() const;
QList messages() const { return m_messages; }
- QList unreadMessages() const;
int unreadCount() const;
int totalCount() const { return m_messages.size(); }
@@ -26,9 +26,6 @@ class MessageStore : public QObject {
void markAllAsRead();
void removeMessage(const QString &id);
- QString highestMessageId() const;
- int maxMessages() const { return m_maxMessages; }
-
bool containsMessage(const QString &id) const;
private:
@@ -36,6 +33,7 @@ class MessageStore : public QObject {
QString storagePath() const;
QList m_messages;
+ QSet m_idSet;
QString m_dataPath;
int m_maxMessages;
};
diff --git a/src/notificationmanager.cpp b/src/notificationmanager.cpp
index 9c28ece..fe44030 100644
--- a/src/notificationmanager.cpp
+++ b/src/notificationmanager.cpp
@@ -1,4 +1,5 @@
#include "notificationmanager.h"
+#include "constants.h"
#include
#include
#include
@@ -9,6 +10,7 @@
#include
#include
#include
+#include
Q_LOGGING_CATEGORY(lcNotification, "com.zackslash.sailpush.notification")
@@ -23,8 +25,11 @@ NotificationManager::NotificationManager(const QString &cachePath, QObject *pare
, m_cachePath(cachePath)
{
QDBusConnection::sessionBus().connect(DBUS_SERVICE, DBUS_PATH, DBUS_IFACE,
- "ActionInvoked", this,
- SLOT(onActionInvoked(uint, QString)));
+ "ActionInvoked", this,
+ SLOT(onActionInvoked(uint, QString)));
+ QDBusConnection::sessionBus().connect(DBUS_SERVICE, DBUS_PATH, DBUS_IFACE,
+ "NotificationClosed", this,
+ SLOT(onNotificationClosed(uint, uint)));
}
void NotificationManager::publishNotification(const Message &msg, int unreadCount, bool displayOn)
@@ -116,6 +121,13 @@ void NotificationManager::onActionInvoked(uint id, const QString &actionKey)
}
}
+void NotificationManager::onNotificationClosed(uint id, uint reason)
+{
+ Q_UNUSED(reason);
+ m_notificationMessageMap.remove(id);
+ m_notificationReceiptMap.remove(id);
+}
+
QString NotificationManager::truncate(const QString &text, int maxLength)
{
if (text.length() <= maxLength) return text;
@@ -146,13 +158,24 @@ QString NotificationManager::stripHtml(const QString &html)
text.replace("—", "\u2014");
text.replace("–", "\u2013");
text.replace("…", "\u2026");
- // Numeric entities — safe to loop since replacements are single chars
- QRegularExpressionMatch m;
- while ((m = hexEntityRe.match(text)).hasMatch()) {
+ // Numeric entities — single pass using globalMatch, replacing in reverse
+ // order so earlier captured positions remain valid after each replacement.
+ QList hexMatches;
+ auto it = hexEntityRe.globalMatch(text);
+ while (it.hasNext())
+ hexMatches.append(it.next());
+ for (int i = hexMatches.size() - 1; i >= 0; --i) {
+ const auto &m = hexMatches[i];
text.replace(m.capturedStart(), m.capturedLength(),
QChar(m.captured(1).toUInt(nullptr, 16)));
}
- while ((m = decEntityRe.match(text)).hasMatch()) {
+
+ QList decMatches;
+ it = decEntityRe.globalMatch(text);
+ while (it.hasNext())
+ decMatches.append(it.next());
+ for (int i = decMatches.size() - 1; i >= 0; --i) {
+ const auto &m = decMatches[i];
text.replace(m.capturedStart(), m.capturedLength(),
QChar(m.captured(1).toUInt()));
}
@@ -208,7 +231,7 @@ void NotificationManager::trackNotification(uint notifId, const Message &msg)
void NotificationManager::writePendingOpenMessage(const QString &messageId)
{
- QString path = m_cachePath + "/pending_open";
+ QString path = m_cachePath + SailPushPaths::PENDING_OPEN;
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
file.write(messageId.toUtf8());
diff --git a/src/notificationmanager.h b/src/notificationmanager.h
index 5846635..052db8d 100644
--- a/src/notificationmanager.h
+++ b/src/notificationmanager.h
@@ -34,6 +34,7 @@ class NotificationManager : public QObject {
private slots:
void onActionInvoked(uint id, const QString &actionKey);
+ void onNotificationClosed(uint id, uint reason);
private:
void trackNotification(uint notifId, const Message &msg);
diff --git a/src/sailpushclient.cpp b/src/sailpushclient.cpp
index 5c8f500..4649184 100644
--- a/src/sailpushclient.cpp
+++ b/src/sailpushclient.cpp
@@ -21,123 +21,66 @@ static const int REQUEST_TIMEOUT_MS = 30000;
SailPushClient::SailPushClient(QObject *parent)
: QObject(parent)
, m_networkManager(new QNetworkAccessManager(this))
- , m_userAgent(USER_AGENT)
{
}
void SailPushClient::login(const QString &email, const QString &password, const QString &twofa)
{
- QUrl url(loginUrl());
- QNetworkRequest request(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
-
QUrlQuery query;
query.addQueryItem("email", email);
query.addQueryItem("password", password);
if (!twofa.isEmpty()) {
query.addQueryItem("twofa", twofa);
}
-
- QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8());
- QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); });
- reply->setProperty("action", "login");
- connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); });
+ sendRequest(QUrl(loginUrl()), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Login);
}
void SailPushClient::registerDevice(const QString &secret, const QString &deviceName)
{
- QUrl url(registerUrl());
- QNetworkRequest request(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
-
QUrlQuery query;
query.addQueryItem("secret", secret);
query.addQueryItem("name", deviceName);
query.addQueryItem("os", "O");
-
- QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8());
- QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); });
- reply->setProperty("action", "register");
- connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); });
+ sendRequest(QUrl(registerUrl()), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Register);
}
void SailPushClient::downloadMessages(const QString &secret, const QString &deviceId)
{
- QUrl url(messagesUrl(secret, deviceId));
- QNetworkRequest request(url);
- request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
-
- QNetworkReply *reply = m_networkManager->get(request);
- QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); });
- reply->setProperty("action", "download");
- connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); });
+ sendRequest(QUrl(messagesUrl(secret, deviceId)), QByteArray(), Action::Download);
}
void SailPushClient::deleteMessages(const QString &secret, const QString &deviceId, const QString &highestMessageId)
{
- QUrl url(deleteUrl(deviceId));
- QNetworkRequest request(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
-
QUrlQuery query;
query.addQueryItem("secret", secret);
query.addQueryItem("message", highestMessageId);
-
- QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8());
- QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); });
- reply->setProperty("action", "delete");
- connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); });
+ sendRequest(QUrl(deleteUrl(deviceId)), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Delete);
}
void SailPushClient::acknowledgeEmergency(const QString &secret, const QString &receipt)
{
- QUrl url(acknowledgeUrl(receipt));
- QNetworkRequest request(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
-
QUrlQuery query;
query.addQueryItem("secret", secret);
-
- QNetworkReply *reply = m_networkManager->post(request, query.toString(QUrl::FullyEncoded).toUtf8());
- QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); });
- reply->setProperty("action", "acknowledge");
- connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); });
+ sendRequest(QUrl(acknowledgeUrl(receipt)), query.toString(QUrl::FullyEncoded).toUtf8(), Action::Acknowledge);
}
void SailPushClient::onReplyFinished(QNetworkReply *reply)
{
- QString action = reply->property("action").toString();
+ Action action = static_cast(reply->property("action").toInt());
int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray data = reply->readAll();
reply->deleteLater();
// Check for auth failures before generic error handling — Qt treats HTTP 4xx as NoError
- if ((httpStatus == 401 || httpStatus == 403) && (action == "download" || action == "delete" || action == "acknowledge")) {
- qCWarning(lcSailPushClient) << "HTTP" << httpStatus << "for action" << action << "— credentials rejected";
- QString errMsg = QStringLiteral("HTTP %1: credentials rejected").arg(httpStatus);
- if (action == "download") emit messagesDownloadFailed(errMsg);
- else if (action == "delete") emit messageDeleteFailed(errMsg);
- else if (action == "acknowledge") emit emergencyAckFailed(errMsg);
+ if ((httpStatus == 401 || httpStatus == 403) && (action == Action::Download || action == Action::Delete || action == Action::Acknowledge)) {
+ qCWarning(lcSailPushClient) << "HTTP" << httpStatus << "for action" << static_cast(action) << "— credentials rejected";
+ emitError(action, QStringLiteral("HTTP %1: credentials rejected").arg(httpStatus));
return;
}
if (reply->error() != QNetworkReply::NoError) {
qCWarning(lcSailPushClient) << "Network error:" << reply->errorString();
- if (action == "login") {
- emit loginFailed(reply->errorString());
- } else if (action == "register") {
- emit deviceRegistrationFailed(reply->errorString());
- } else if (action == "download") {
- emit messagesDownloadFailed(reply->errorString());
- } else if (action == "delete") {
- emit messageDeleteFailed(reply->errorString());
- } else if (action == "acknowledge") {
- emit emergencyAckFailed(reply->errorString());
- }
+ emitError(action, reply->errorString());
return;
}
@@ -145,18 +88,14 @@ void SailPushClient::onReplyFinished(QNetworkReply *reply)
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qCWarning(lcSailPushClient) << "JSON parse error:" << parseError.errorString();
- if (action == "login") emit loginFailed(tr("Invalid server response"));
- else if (action == "register") emit deviceRegistrationFailed(tr("Invalid server response"));
- else if (action == "download") emit messagesDownloadFailed(tr("Invalid server response"));
- else if (action == "delete") emit messageDeleteFailed(tr("Invalid server response"));
- else if (action == "acknowledge") emit emergencyAckFailed(tr("Invalid server response"));
+ emitError(action, tr("Invalid server response"));
return;
}
QJsonObject root = doc.object();
int status = root.value("status").toInt(0);
- if (action == "login") {
+ if (action == Action::Login) {
if (httpStatus == 412) {
emit twoFactorRequired();
return;
@@ -164,27 +103,17 @@ void SailPushClient::onReplyFinished(QNetworkReply *reply)
if (status == 1) {
emit loginSuccess(root.value("id").toString(), root.value("secret").toString());
} else {
- QJsonArray errors = root.value("errors").toArray();
- QString errorStr;
- for (const QJsonValue &e : errors) {
- if (!errorStr.isEmpty()) errorStr += "; ";
- errorStr += e.toString();
- }
+ QString errorStr = extractErrorMessage(root);
emit loginFailed(errorStr.isEmpty() ? tr("Login failed") : errorStr);
}
- } else if (action == "register") {
+ } else if (action == Action::Register) {
if (status == 1) {
emit deviceRegistered(root.value("id").toString());
} else {
- QJsonArray errors = root.value("errors").toArray();
- QString errorStr;
- for (const QJsonValue &e : errors) {
- if (!errorStr.isEmpty()) errorStr += "; ";
- errorStr += e.toString();
- }
+ QString errorStr = extractErrorMessage(root);
emit deviceRegistrationFailed(errorStr.isEmpty() ? tr("Registration failed") : errorStr);
}
- } else if (action == "download") {
+ } else if (action == Action::Download) {
if (status == 1) {
QList messages;
QJsonArray msgs = root.value("messages").toArray();
@@ -193,41 +122,65 @@ void SailPushClient::onReplyFinished(QNetworkReply *reply)
}
emit messagesDownloaded(messages);
} else {
- QJsonArray errors = root.value("errors").toArray();
- QString errorStr;
- for (const QJsonValue &e : errors) {
- if (!errorStr.isEmpty()) errorStr += "; ";
- errorStr += e.toString();
- }
+ QString errorStr = extractErrorMessage(root);
emit messagesDownloadFailed(errorStr.isEmpty() ? tr("Download failed") : errorStr);
}
- } else if (action == "delete") {
+ } else if (action == Action::Delete) {
if (status == 1) {
emit messagesDeleted();
} else {
- QJsonArray errors = root.value("errors").toArray();
- QString errorStr;
- for (const QJsonValue &e : errors) {
- if (!errorStr.isEmpty()) errorStr += "; ";
- errorStr += e.toString();
- }
+ QString errorStr = extractErrorMessage(root);
emit messageDeleteFailed(errorStr.isEmpty() ? tr("Delete failed") : errorStr);
}
- } else if (action == "acknowledge") {
+ } else if (action == Action::Acknowledge) {
if (status == 1) {
emit emergencyAcknowledged();
} else {
- QJsonArray errors = root.value("errors").toArray();
- QString errorStr;
- for (const QJsonValue &e : errors) {
- if (!errorStr.isEmpty()) errorStr += "; ";
- errorStr += e.toString();
- }
+ QString errorStr = extractErrorMessage(root);
emit emergencyAckFailed(errorStr.isEmpty() ? tr("Acknowledge failed") : errorStr);
}
}
}
+QNetworkReply* SailPushClient::sendRequest(const QUrl &url, const QByteArray &postData, Action action)
+{
+ QNetworkRequest request(url);
+ if (!postData.isEmpty()) {
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+ }
+ request.setHeader(QNetworkRequest::UserAgentHeader, USER_AGENT);
+
+ QNetworkReply *reply = postData.isEmpty()
+ ? m_networkManager->get(request)
+ : m_networkManager->post(request, postData);
+ QTimer::singleShot(REQUEST_TIMEOUT_MS, reply, [reply]() { if (reply->isRunning()) reply->abort(); });
+ reply->setProperty("action", static_cast(action));
+ connect(reply, &QNetworkReply::finished, this, [this, reply]() { onReplyFinished(reply); });
+ return reply;
+}
+
+void SailPushClient::emitError(Action action, const QString &message)
+{
+ switch (action) {
+ case Action::Login: emit loginFailed(message); break;
+ case Action::Register: emit deviceRegistrationFailed(message); break;
+ case Action::Download: emit messagesDownloadFailed(message); break;
+ case Action::Delete: emit messageDeleteFailed(message); break;
+ case Action::Acknowledge: emit emergencyAckFailed(message); break;
+ }
+}
+
+QString SailPushClient::extractErrorMessage(const QJsonObject &root)
+{
+ QJsonArray errors = root.value("errors").toArray();
+ QString errorStr;
+ for (const QJsonValue &e : errors) {
+ if (!errorStr.isEmpty()) errorStr += "; ";
+ errorStr += e.toString();
+ }
+ return errorStr;
+}
+
QString SailPushClient::loginUrl() { return QStringLiteral("%1/users/login.json").arg(API_BASE); }
QString SailPushClient::registerUrl() { return QStringLiteral("%1/devices.json").arg(API_BASE); }
// Note: secret is in query string — avoid logging this URL
diff --git a/src/sailpushclient.h b/src/sailpushclient.h
index 0d1b40e..c3efdc8 100644
--- a/src/sailpushclient.h
+++ b/src/sailpushclient.h
@@ -45,8 +45,19 @@ private slots:
void onReplyFinished(QNetworkReply *reply);
private:
+ enum class Action {
+ Login,
+ Register,
+ Download,
+ Delete,
+ Acknowledge
+ };
+
+ QNetworkReply* sendRequest(const QUrl &url, const QByteArray &postData, Action action);
+ void emitError(Action action, const QString &message);
+ static QString extractErrorMessage(const QJsonObject &root);
+
QNetworkAccessManager *m_networkManager;
- QString m_userAgent;
};
#endif // SAILPUSHCLIENT_H
diff --git a/src/soundplayer.cpp b/src/soundplayer.cpp
index bc43d3c..fa03ee5 100644
--- a/src/soundplayer.cpp
+++ b/src/soundplayer.cpp
@@ -56,7 +56,9 @@ bool SoundPlayer::soundExists(const QString &soundName, const QString &cacheDir)
void SoundPlayer::downloadSound(const QString &soundName, const QString &cacheDir)
{
- m_downloadQueue.enqueue(qMakePair(soundName, cacheDir));
+ QPair entry = qMakePair(soundName, cacheDir);
+ if (m_downloadQueue.contains(entry)) return;
+ m_downloadQueue.enqueue(entry);
if (!m_downloading) {
processNextDownload();
}
diff --git a/src/websocketmanager.cpp b/src/websocketmanager.cpp
index 8e6a716..503dfb9 100644
--- a/src/websocketmanager.cpp
+++ b/src/websocketmanager.cpp
@@ -17,8 +17,8 @@ WebSocketManager::WebSocketManager(QObject *parent)
connect(m_webSocket, &QWebSocket::disconnected, this, &WebSocketManager::onDisconnected);
connect(m_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketManager::onTextMessageReceived);
connect(m_webSocket, &QWebSocket::binaryMessageReceived, this, &WebSocketManager::onBinaryMessageReceived);
- connect(m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)),
- this, SLOT(onError(QAbstractSocket::SocketError)));
+ connect(m_webSocket, QOverload::of(&QWebSocket::error),
+ this, &WebSocketManager::onError);
connect(m_reconnectTimer, &QTimer::timeout, this, &WebSocketManager::onReconnectTimer);
}
@@ -46,6 +46,8 @@ void WebSocketManager::disconnectFromServer()
m_autoReconnect = false;
m_reconnectTimer->stop();
m_webSocket->close();
+ m_secret.clear();
+ m_deviceId.clear();
setState(ConnectionState::Disconnected);
}
@@ -75,9 +77,19 @@ void WebSocketManager::onDisconnected()
void WebSocketManager::onTextMessageReceived(const QString &message)
{
- FrameType frame = static_cast(parseFrame(message.toUtf8()).toInt());
+ FrameType frame = static_cast(parseFrame(message.toUtf8()));
+ handleFrame(frame);
+}
- switch (frame) {
+void WebSocketManager::onBinaryMessageReceived(const QByteArray &message)
+{
+ FrameType frame = static_cast(parseFrame(message));
+ handleFrame(frame);
+}
+
+void WebSocketManager::handleFrame(FrameType type)
+{
+ switch (type) {
case FrameType::NewMessage:
qCInfo(lcWebSocket) << "New message available";
emit newMessageAvailable();
@@ -99,31 +111,7 @@ void WebSocketManager::onTextMessageReceived(const QString &message)
case FrameType::KeepAlive:
break;
case FrameType::Unknown:
- qCDebug(lcWebSocket) << "Unknown frame received:" << message;
- break;
- }
-}
-
-void WebSocketManager::onBinaryMessageReceived(const QByteArray &message)
-{
- FrameType frame = static_cast(parseFrame(message).toInt());
-
- switch (frame) {
- case FrameType::NewMessage:
- emit newMessageAvailable();
- break;
- case FrameType::Reload:
- emit reloadRequested();
- break;
- case FrameType::Error:
- m_autoReconnect = false;
- emit connectionError(QStringLiteral("Permanent server error."));
- break;
- case FrameType::SessionClosedByServer:
- m_autoReconnect = false;
- emit sessionClosedByServer();
- break;
- default:
+ qCDebug(lcWebSocket) << "Unknown frame received";
break;
}
}
@@ -170,7 +158,7 @@ void WebSocketManager::setState(ConnectionState newState)
}
}
-QVariant WebSocketManager::parseFrame(const QByteArray &data)
+int WebSocketManager::parseFrame(const QByteArray &data)
{
if (data.isEmpty()) return static_cast(FrameType::Unknown);
diff --git a/src/websocketmanager.h b/src/websocketmanager.h
index 05771d1..419b9fb 100644
--- a/src/websocketmanager.h
+++ b/src/websocketmanager.h
@@ -2,7 +2,6 @@
#define WEBSOCKETMANAGER_H
#include
-#include
#include
#include
#include
@@ -65,7 +64,8 @@ private slots:
void sendLoginFrame();
void scheduleReconnect();
void setState(ConnectionState newState);
- Q_INVOKABLE QVariant parseFrame(const QByteArray &data);
+ Q_INVOKABLE int parseFrame(const QByteArray &data);
+ void handleFrame(FrameType type);
QWebSocket *m_webSocket;
QTimer *m_reconnectTimer;
diff --git a/tests/tst_credentialstore.cpp b/tests/tst_credentialstore.cpp
index a04b654..262dd80 100644
--- a/tests/tst_credentialstore.cpp
+++ b/tests/tst_credentialstore.cpp
@@ -47,28 +47,26 @@ void TestCredentialStore::testNoCredentialsInitially()
void TestCredentialStore::testSaveAndLoad()
{
- QVERIFY(m_store->save("secret123", "device456", "user789", "MyDevice"));
+ QVERIFY(m_store->save("secret123", "device456"));
- QString secret, deviceId, userKey, deviceName;
- QVERIFY(m_store->load(secret, deviceId, userKey, deviceName));
+ QString secret, deviceId;
+ QVERIFY(m_store->load(secret, deviceId));
QCOMPARE(secret, QString("secret123"));
QCOMPARE(deviceId, QString("device456"));
- QCOMPARE(userKey, QString("user789"));
- QCOMPARE(deviceName, QString("MyDevice"));
}
void TestCredentialStore::testHasCredentials()
{
QVERIFY(!m_store->hasCredentials());
- m_store->save("secret", "device", "user", "name");
+ m_store->save("secret", "device");
QVERIFY(m_store->hasCredentials());
}
void TestCredentialStore::testClear()
{
- m_store->save("secret", "device", "user", "name");
+ m_store->save("secret", "device");
QVERIFY(m_store->hasCredentials());
QVERIFY(m_store->clear());
@@ -77,48 +75,48 @@ void TestCredentialStore::testClear()
void TestCredentialStore::testOverwrite()
{
- m_store->save("secret1", "device1", "user1", "name1");
+ m_store->save("secret1", "device1");
- QString secret, deviceId, userKey, deviceName;
- m_store->load(secret, deviceId, userKey, deviceName);
+ QString secret, deviceId;
+ m_store->load(secret, deviceId);
QCOMPARE(secret, QString("secret1"));
- m_store->save("secret2", "device2", "user2", "name2");
- m_store->load(secret, deviceId, userKey, deviceName);
+ m_store->save("secret2", "device2");
+ m_store->load(secret, deviceId);
QCOMPARE(secret, QString("secret2"));
QCOMPARE(deviceId, QString("device2"));
}
void TestCredentialStore::testEmptyValues()
{
- m_store->save("", "", "", "");
+ m_store->save("", "");
- QString secret, deviceId, userKey, deviceName;
- QVERIFY(!m_store->load(secret, deviceId, userKey, deviceName));
+ QString secret, deviceId;
+ QVERIFY(!m_store->load(secret, deviceId));
}
void TestCredentialStore::testLoadNonexistent()
{
FileCredentialStore *emptyStore = new FileCredentialStore("/nonexistent/path/that/does/not/exist", this);
- QString secret, deviceId, userKey, deviceName;
- QVERIFY(!emptyStore->load(secret, deviceId, userKey, deviceName));
+ QString secret, deviceId;
+ QVERIFY(!emptyStore->load(secret, deviceId));
QCOMPARE(emptyStore->lastError(), FileCredentialStore::LoadError::SecretNotFound);
delete emptyStore;
}
void TestCredentialStore::testLastErrorNoneOnSuccess()
{
- m_store->save("secret", "device", "user", "name");
+ m_store->save("secret", "device");
- QString secret, deviceId, userKey, deviceName;
- QVERIFY(m_store->load(secret, deviceId, userKey, deviceName));
+ QString secret, deviceId;
+ QVERIFY(m_store->load(secret, deviceId));
QCOMPARE(m_store->lastError(), FileCredentialStore::LoadError::None);
}
void TestCredentialStore::testLastErrorSecretNotFound()
{
- QString secret, deviceId, userKey, deviceName;
- QVERIFY(!m_store->load(secret, deviceId, userKey, deviceName));
+ QString secret, deviceId;
+ QVERIFY(!m_store->load(secret, deviceId));
QCOMPARE(m_store->lastError(), FileCredentialStore::LoadError::SecretNotFound);
}
@@ -128,8 +126,6 @@ void TestCredentialStore::testLastErrorInvalidFormat()
obj.insert("version", 1);
obj.insert("secret", "encrypted");
obj.insert("device_id", "encrypted");
- obj.insert("user_key", "encrypted");
- obj.insert("device_name", "encrypted");
QJsonDocument doc(obj);
QString path = m_tempDir.path() + "/credentials.json";
@@ -138,14 +134,14 @@ void TestCredentialStore::testLastErrorInvalidFormat()
file.write(doc.toJson(QJsonDocument::Compact));
file.close();
- QString secret, deviceId, userKey, deviceName;
- QVERIFY(!m_store->load(secret, deviceId, userKey, deviceName));
+ QString secret, deviceId;
+ QVERIFY(!m_store->load(secret, deviceId));
QCOMPARE(m_store->lastError(), FileCredentialStore::LoadError::InvalidFormat);
}
void TestCredentialStore::testFilePermissionsAfterSave()
{
- m_store->save("secret", "device", "user", "name");
+ m_store->save("secret", "device");
QFile::Permissions perms = QFile::permissions(m_tempDir.path() + "/credentials.json");
QFile::Permissions expected = QFile::ReadOwner | QFile::WriteOwner
| QFile::ReadUser | QFile::WriteUser;
diff --git a/tests/tst_messagestore.cpp b/tests/tst_messagestore.cpp
index f84b262..f88bbc8 100644
--- a/tests/tst_messagestore.cpp
+++ b/tests/tst_messagestore.cpp
@@ -17,8 +17,8 @@ private slots:
void testMarkAsRead();
void testMarkAllAsRead();
void testRemoveMessage();
+ void testContainsAfterRemove();
void testUnreadCount();
- void testHighestMessageId();
void testContainsMessage();
void testTrimMessages();
void testSaveAndLoad();
@@ -44,7 +44,6 @@ void TestMessageStore::testEmptyStore()
QCOMPARE(m_store->messages().size(), 0);
QCOMPARE(m_store->unreadCount(), 0);
QCOMPARE(m_store->totalCount(), 0);
- QCOMPARE(m_store->highestMessageId(), QString());
}
void TestMessageStore::testAddMessage()
@@ -129,6 +128,20 @@ void TestMessageStore::testRemoveMessage()
QCOMPARE(m_store->messages().size(), 0);
}
+void TestMessageStore::testContainsAfterRemove()
+{
+ Message msg;
+ msg.id = "msg1";
+ m_store->addMessage(msg);
+
+ QVERIFY(m_store->containsMessage("msg1"));
+
+ m_store->removeMessage("msg1");
+
+ QVERIFY(!m_store->containsMessage("msg1"));
+ QCOMPARE(m_store->messages().size(), 0);
+}
+
void TestMessageStore::testUnreadCount()
{
Message m1, m2, m3;
@@ -143,18 +156,6 @@ void TestMessageStore::testUnreadCount()
QCOMPARE(m_store->unreadCount(), 2);
}
-void TestMessageStore::testHighestMessageId()
-{
- Message m1, m2;
- m1.id = "1";
- m2.id = "2";
-
- m_store->addMessage(m1);
- m_store->addMessage(m2);
-
- QCOMPARE(m_store->highestMessageId(), QString("2"));
-}
-
void TestMessageStore::testContainsMessage()
{
Message msg;
diff --git a/tests/tst_websocketmanager.cpp b/tests/tst_websocketmanager.cpp
index a351c12..30b399c 100644
--- a/tests/tst_websocketmanager.cpp
+++ b/tests/tst_websocketmanager.cpp
@@ -24,12 +24,12 @@ void TestWebSocketManager::testParseKeepAlive()
{
m_manager = new WebSocketManager(this);
- QVariant result;
+ int result;
QMetaObject::invokeMethod(m_manager, "parseFrame",
- Q_RETURN_ARG(QVariant, result),
+ Q_RETURN_ARG(int, result),
Q_ARG(QByteArray, QByteArray("#")));
- QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::KeepAlive));
+ QCOMPARE(result, static_cast(WebSocketManager::FrameType::KeepAlive));
delete m_manager;
}
@@ -37,12 +37,12 @@ void TestWebSocketManager::testParseNewMessage()
{
m_manager = new WebSocketManager(this);
- QVariant result;
+ int result;
QMetaObject::invokeMethod(m_manager, "parseFrame",
- Q_RETURN_ARG(QVariant, result),
+ Q_RETURN_ARG(int, result),
Q_ARG(QByteArray, QByteArray("!")));
- QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::NewMessage));
+ QCOMPARE(result, static_cast(WebSocketManager::FrameType::NewMessage));
delete m_manager;
}
@@ -50,12 +50,12 @@ void TestWebSocketManager::testParseReload()
{
m_manager = new WebSocketManager(this);
- QVariant result;
+ int result;
QMetaObject::invokeMethod(m_manager, "parseFrame",
- Q_RETURN_ARG(QVariant, result),
+ Q_RETURN_ARG(int, result),
Q_ARG(QByteArray, QByteArray("R")));
- QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Reload));
+ QCOMPARE(result, static_cast(WebSocketManager::FrameType::Reload));
delete m_manager;
}
@@ -63,12 +63,12 @@ void TestWebSocketManager::testParseError()
{
m_manager = new WebSocketManager(this);
- QVariant result;
+ int result;
QMetaObject::invokeMethod(m_manager, "parseFrame",
- Q_RETURN_ARG(QVariant, result),
+ Q_RETURN_ARG(int, result),
Q_ARG(QByteArray, QByteArray("E")));
- QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Error));
+ QCOMPARE(result, static_cast(WebSocketManager::FrameType::Error));
delete m_manager;
}
@@ -76,12 +76,12 @@ void TestWebSocketManager::testParseSessionClosed()
{
m_manager = new WebSocketManager(this);
- QVariant result;
+ int result;
QMetaObject::invokeMethod(m_manager, "parseFrame",
- Q_RETURN_ARG(QVariant, result),
+ Q_RETURN_ARG(int, result),
Q_ARG(QByteArray, QByteArray("A")));
- QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::SessionClosedByServer));
+ QCOMPARE(result, static_cast(WebSocketManager::FrameType::SessionClosedByServer));
delete m_manager;
}
@@ -89,12 +89,12 @@ void TestWebSocketManager::testParseUnknown()
{
m_manager = new WebSocketManager(this);
- QVariant result;
+ int result;
QMetaObject::invokeMethod(m_manager, "parseFrame",
- Q_RETURN_ARG(QVariant, result),
+ Q_RETURN_ARG(int, result),
Q_ARG(QByteArray, QByteArray("X")));
- QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Unknown));
+ QCOMPARE(result, static_cast(WebSocketManager::FrameType::Unknown));
delete m_manager;
}
@@ -102,12 +102,12 @@ void TestWebSocketManager::testParseEmpty()
{
m_manager = new WebSocketManager(this);
- QVariant result;
+ int result;
QMetaObject::invokeMethod(m_manager, "parseFrame",
- Q_RETURN_ARG(QVariant, result),
+ Q_RETURN_ARG(int, result),
Q_ARG(QByteArray, QByteArray()));
- QCOMPARE(result.toInt(), static_cast(WebSocketManager::FrameType::Unknown));
+ QCOMPARE(result, static_cast(WebSocketManager::FrameType::Unknown));
delete m_manager;
}
diff --git a/translations/sailpush_be.ts b/translations/sailpush_be.ts
index a848abf..ef7bb4d 100644
--- a/translations/sailpush_be.ts
+++ b/translations/sailpush_be.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Захаваныя ўліковыя даныя не ўдалося расшыфраваць. Гэта можа здарыцца пасля абнаўлення сістэмы. Калі ласка, увайдзіце зноў.
+
+ Secrets service is not available. Please restart the device and try again.
+ Служба secrets недаступная. Перезагрузіце прыладу і паспрабуйце зноў.
+
LoginPage
@@ -305,7 +309,7 @@ mcetool --set-suspend-policy=early
Polling Fallback
- Рэзервовы апытанне
+ Рэзервовае апытанне
Polling Interval
diff --git a/translations/sailpush_bg.ts b/translations/sailpush_bg.ts
index 26c834e..e9dd24f 100644
--- a/translations/sailpush_bg.ts
+++ b/translations/sailpush_bg.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Запазените данни за вход не могат да бъдат декриптирани. Това може да се случи след актуализация на системата. Моля, влезте отново.
+
+ Secrets service is not available. Please restart the device and try again.
+ Услугата secrets не е налична. Рестартирайте устройството и опитайте отново.
+
LoginPage
diff --git a/translations/sailpush_bs.ts b/translations/sailpush_bs.ts
index f35b071..f67643d 100644
--- a/translations/sailpush_bs.ts
+++ b/translations/sailpush_bs.ts
@@ -52,7 +52,7 @@
LoginHelper
App was upgraded — saved credentials use an older format and cannot be migrated. Please log in again.
- Aplikacija je ažurirana — sačuvani akreditive koriste stariji format i ne mogu se migrirati. Molimo prijavite se ponovo.
+ Aplikacija je ažurirana — sačuvani akreditivi koriste stariji format i ne mogu se migrirati. Molimo prijavite se ponovo.
Failed to save credentials
@@ -60,7 +60,11 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
- Sačuvani akreditive nisu mogli biti dešifrirani. Ovo se može desiti nakon ažuriranja sistema. Molimo prijavite se ponovo.
+ Sačuvani akreditivi nisu mogli biti dešifrirani. Ovo se može desiti nakon ažuriranja sistema. Molimo prijavite se ponovo.
+
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets usluga nije dostupna. Ponovo pokrenite uređaj i pokušajte ponovo.
diff --git a/translations/sailpush_ca.ts b/translations/sailpush_ca.ts
index 4625545..7a5f234 100644
--- a/translations/sailpush_ca.ts
+++ b/translations/sailpush_ca.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Les credencials desades no s'han pogut desxifrar. Això pot passar després d'una actualització del sistema. Torna a iniciar sessió.
+
+ Secrets service is not available. Please restart the device and try again.
+ El servei de secrets no està disponible. Reinicieu el dispositiu i torneu-ho a provar.
+
LoginPage
diff --git a/translations/sailpush_cs.ts b/translations/sailpush_cs.ts
index 9cc06bd..da53294 100644
--- a/translations/sailpush_cs.ts
+++ b/translations/sailpush_cs.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Uložené přihlašovací údaje nelze dešifrovat. K tomu může dojít po aktualizaci systému. Přihlaste se znovu.
+
+ Secrets service is not available. Please restart the device and try again.
+ Služba secrets není k dispozici. Restartujte zařízení a zkuste to znovu.
+
LoginPage
diff --git a/translations/sailpush_cy.ts b/translations/sailpush_cy.ts
index 6bd53d0..1ed59d7 100644
--- a/translations/sailpush_cy.ts
+++ b/translations/sailpush_cy.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Ni ellid datgryptio'r manylion a gadwyd. Gall hyn ddigwydd ar ôl diweddariad system. Mewngofnodwch eto os gwelwch yn dda.
+
+ Secrets service is not available. Please restart the device and try again.
+ Nid yw gwasanaeth secrets ar gael. Ail-gychwynnwch y ddyfais a rhowch gynnig arall arni.
+
LoginPage
diff --git a/translations/sailpush_da.ts b/translations/sailpush_da.ts
index ec5610b..8755b2b 100644
--- a/translations/sailpush_da.ts
+++ b/translations/sailpush_da.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Gemte loginoplysninger kunne ikke dekrypteres. Dette kan ske efter en systemopdatering. Log venligst ind igen.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets-tjenesten er ikke tilgængelig. Genstart enheden og prøv igen.
+
LoginPage
diff --git a/translations/sailpush_de.ts b/translations/sailpush_de.ts
index 4c128b2..761c46f 100644
--- a/translations/sailpush_de.ts
+++ b/translations/sailpush_de.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Gespeicherte Anmeldedaten konnten nicht entschlüsselt werden. Dies kann nach einem Systemupdate passieren. Bitte erneut anmelden.
+
+ Secrets service is not available. Please restart the device and try again.
+ Der Secrets-Dienst ist nicht verfügbar. Bitte starten Sie das Gerät neu und versuchen Sie es erneut.
+
LoginPage
diff --git a/translations/sailpush_el.ts b/translations/sailpush_el.ts
index 5cae5c6..d5c01ca 100644
--- a/translations/sailpush_el.ts
+++ b/translations/sailpush_el.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Τα αποθηκευμένα διαπιστευτήρια δεν μπόρεσαν να αποκρυπτογραφηθούν. Αυτό μπορεί να συμβεί μετά από ενημέρωση συστήματος. Παρακαλώ συνδεθείτε ξανά.
+
+ Secrets service is not available. Please restart the device and try again.
+ Η υπηρεσία secrets δεν είναι διαθέσιμη. Επανεκκινήστε τη συσκευή και δοκιμάστε ξανά.
+
LoginPage
diff --git a/translations/sailpush_es.ts b/translations/sailpush_es.ts
index d0b8382..a4afa5c 100644
--- a/translations/sailpush_es.ts
+++ b/translations/sailpush_es.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Las credenciales guardadas no se pudieron descifrar. Esto puede ocurrir después de una actualización del sistema. Vuelve a iniciar sesión.
+
+ Secrets service is not available. Please restart the device and try again.
+ El servicio de secretos no está disponible. Reinicie el dispositivo e inténtelo de nuevo.
+
LoginPage
diff --git a/translations/sailpush_et.ts b/translations/sailpush_et.ts
index abe462a..dd267cd 100644
--- a/translations/sailpush_et.ts
+++ b/translations/sailpush_et.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Salvestatud mandaate ei saanud dekrüptida. See võib juhtuda pärast süsteemiuuendust. Palun logige uuesti sisse.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets-teenus pole saadaval. Taaskäivitage seade ja proovige uuesti.
+
LoginPage
diff --git a/translations/sailpush_eu.ts b/translations/sailpush_eu.ts
index dac85b6..7b55fc1 100644
--- a/translations/sailpush_eu.ts
+++ b/translations/sailpush_eu.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Gordetako kredentzialak ezin izan dira deszifratu. Hau sistema eguneratu baten ondoren gerta daiteke. Hasi saioa berriro.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets zerbitzua ez dago erabilgarri. Berrabiarazi gailua eta saiatu berriro.
+
LoginPage
diff --git a/translations/sailpush_fi.ts b/translations/sailpush_fi.ts
index 08bd558..f928525 100644
--- a/translations/sailpush_fi.ts
+++ b/translations/sailpush_fi.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Tallennettuja kirjautumistietoja ei voitu purkaa. Tämä voi tapahtua järjestelmäpäivityksen jälkeen. Kirjaudu sisään uudelleen.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets-palvelu ei ole käytettävissä. Käynnistä laite uudelleen ja yritä uudelleen.
+
LoginPage
diff --git a/translations/sailpush_fr.ts b/translations/sailpush_fr.ts
index 4db3e36..1068c6e 100644
--- a/translations/sailpush_fr.ts
+++ b/translations/sailpush_fr.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Les identifiants enregistrés n'ont pas pu être déchiffrés. Cela peut survenir après une mise à jour système. Veuillez vous reconnecter.
+
+ Secrets service is not available. Please restart the device and try again.
+ Le service de secrets n'est pas disponible. Veuillez redémarrer l'appareil et réessayer.
+
LoginPage
diff --git a/translations/sailpush_ga.ts b/translations/sailpush_ga.ts
index 006f77d..f2fd29c 100644
--- a/translations/sailpush_ga.ts
+++ b/translations/sailpush_ga.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Níorbh fhéidir na creidiúintí sábháilte a dhíchriptiú. Is féidir é seo tarlú tar éis nuashonrú córais. Logáil isteach arís le do thoil.
+
+ Secrets service is not available. Please restart the device and try again.
+ Níl seirbhís secrets ar fáil. Atosaigh an gléas agus bain triail as arís.
+
LoginPage
diff --git a/translations/sailpush_gl.ts b/translations/sailpush_gl.ts
index 99b9996..0bd7cd7 100644
--- a/translations/sailpush_gl.ts
+++ b/translations/sailpush_gl.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
As credenciais gardadas non puideron ser descifradas. Isto pode acontecer tras unha actualización do sistema. Inicia sesión novamente.
+
+ Secrets service is not available. Please restart the device and try again.
+ O servizo de secrets non está dispoñible. Reinicia o dispositivo e téntao de novo.
+
LoginPage
diff --git a/translations/sailpush_hr.ts b/translations/sailpush_hr.ts
index 53bc82c..e604d6a 100644
--- a/translations/sailpush_hr.ts
+++ b/translations/sailpush_hr.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Spremljene vjerodajnice nije moguće dešifrirati. To se može dogoditi nakon ažuriranja sustava. Prijavite se ponovo.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets usluga nije dostupna. Ponovno pokrenite uređaj i pokušajte ponovno.
+
LoginPage
@@ -143,11 +147,11 @@ Povucite dolje za sinkronizaciju
MessageDelegate
%1h ago
- pri %1h
+ prije %1h
%1m ago
- pri %1m
+ prije %1m
Delete
diff --git a/translations/sailpush_hu.ts b/translations/sailpush_hu.ts
index 0eaada1..844ce54 100644
--- a/translations/sailpush_hu.ts
+++ b/translations/sailpush_hu.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
A mentett hitelesítő adatok nem fejthetők vissza. Ez rendszerfrissítés után fordulhat elő. Kérjük, jelentkezzen be újra.
+
+ Secrets service is not available. Please restart the device and try again.
+ A secrets szolgáltatás nem érhető el. Kérjük, indítsa újra az eszközt és próbálja újra.
+
LoginPage
diff --git a/translations/sailpush_hy.ts b/translations/sailpush_hy.ts
index d033aaa..0aebd1b 100644
--- a/translations/sailpush_hy.ts
+++ b/translations/sailpush_hy.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Պահպանված մուտքագրման տվյալները չհաջողվեց վերծանել: Սա կարող է տեղի ունենալ համակարգի թարմացումից հետո: Խնդրում ենք կրկին մուտք գործել:
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets ծառայությունը հասանելի չէ: Վերագործարկեք սարքը և կրկին փորձեք:
+
LoginPage
@@ -337,7 +341,7 @@ mcetool --set-suspend-policy=early
This will clear your credentials and stop the background service. Are you sure?
- Սա կջնջի Ձեր մուտքագրման տվյալները և կդադարեցնի ֆոնային ծառայությունը: Համոզված եք:
+ Սա կջնջի Ձեր մուտքագրման տվյալները և կդադարեցնի ֆոնային ծառայությունը: Համոզված ե՞ք
Unofficial Pushover Open Client for SailfishOS.
diff --git a/translations/sailpush_is.ts b/translations/sailpush_is.ts
index ce6d988..7431527 100644
--- a/translations/sailpush_is.ts
+++ b/translations/sailpush_is.ts
@@ -60,7 +60,11 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
- Ekki var hægt að afrita vistuð skilríki. Þetta getur gerst eftir kerfisuppfærslu. Vinsamlegast skráðu þig inn aftur.
+ Ekki var hægt að dulráða vistuð skilríki. Þetta getur gerst eftir kerfisuppfærslu. Vinsamlegast skráðu þig inn aftur.
+
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets-þjónustan er ekki tiltæk. Endurræstu tækið og reyndu aftur.
@@ -283,7 +287,7 @@ Dragðu niður til að samstilla
If WebSocket disconnects for 30s, falls back to periodic polling for notifications.
- Ef WebSocket aftengist í 30 sekúndur, fellur á aftur á tímabundið polling fyrir tilkynningar.
+ Ef WebSocket aftengist í 30 sekúndur, fellur aftur á tímabundið polling fyrir tilkynningar.
Keeps CPU awake during message sync to ensure reliable delivery. Uses MCE keepalive API.
diff --git a/translations/sailpush_it.ts b/translations/sailpush_it.ts
index bd9a3f3..6591e8d 100644
--- a/translations/sailpush_it.ts
+++ b/translations/sailpush_it.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Le credenziali salvate non possono essere decriptate. Questo può accadere dopo un aggiornamento di sistema. Effettua nuovamente l'accesso.
+
+ Secrets service is not available. Please restart the device and try again.
+ Il servizio secrets non è disponibile. Riavvia il dispositivo e riprova.
+
LoginPage
diff --git a/translations/sailpush_ka.ts b/translations/sailpush_ka.ts
index cce211d..ae3d402 100644
--- a/translations/sailpush_ka.ts
+++ b/translations/sailpush_ka.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
შენახული მონაცემების გაშიფვრა ვერ მოხერხდა. ეს შეიძლება მოხდეს სისტემის განახლების შემდეგ. გთხოვთ, ხელახლა შეხვიდეთ.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets სერვისი ხელმისაწვდომი არ არის. გადატვირთეთ მოწყობილობა და კიდევ სცადეთ.
+
LoginPage
@@ -170,7 +174,7 @@ Pull down to sync
Deleting
- წაშლა
+ წაშლა მიმდინარეობს
Message
diff --git a/translations/sailpush_lt.ts b/translations/sailpush_lt.ts
index 973e68d..f35f03d 100644
--- a/translations/sailpush_lt.ts
+++ b/translations/sailpush_lt.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Išsaugoti prisijungimo duomenys negalėjo būti iššifruoti. Tai gali nutikti po sistemos atnaujinimo. Prašome prisijungti iš naujo.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets paslauga neprieinama. Paleiskite įrenginį iš naujo ir bandykite dar kartą.
+
LoginPage
diff --git a/translations/sailpush_lv.ts b/translations/sailpush_lv.ts
index 9cd97f5..4f1206b 100644
--- a/translations/sailpush_lv.ts
+++ b/translations/sailpush_lv.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Saglabātās akreditācijas nevarēja atšifrēt. Tas var notikt pēc sistēmas atjauninājuma. Lūdzu, piesakieties vēlreiz.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets pakalpojums nav pieejams. Restartējiet ierīci un mēģiniet vēlreiz.
+
LoginPage
@@ -275,7 +279,7 @@ Velciet lejup, lai sinhronizētu
Deep Sleep
- Dziļais miegs
+ Dziļais miegs
Enable Polling Fallback
diff --git a/translations/sailpush_mk.ts b/translations/sailpush_mk.ts
index 8a0f830..2f68fa3 100644
--- a/translations/sailpush_mk.ts
+++ b/translations/sailpush_mk.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Зачуваните акредитиви не можеа да се декриптираат. Ова може да се случи по ажурирање на системот. Ве молиме, најавете се повторно.
+
+ Secrets service is not available. Please restart the device and try again.
+ Сервисот secrets не е достапен. Рестартирајте го уредот и обидете се повторно.
+
LoginPage
diff --git a/translations/sailpush_mt.ts b/translations/sailpush_mt.ts
index a0a62df..3e6dc1a 100644
--- a/translations/sailpush_mt.ts
+++ b/translations/sailpush_mt.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Il-kredenzjali maħżuna ma setgħux jiġu deċifrati. Dan jista' jiġri wara aġġornament tas-sistema. Jekk jogħġbok erġa' idħol.
+
+ Secrets service is not available. Please restart the device and try again.
+ Is-servizz tas-secrets mhuwiex disponibbli. Erġa' ibda l-apparat u erġa' pprova.
+
LoginPage
diff --git a/translations/sailpush_nb.ts b/translations/sailpush_nb.ts
index 0a7ce26..82ba47c 100644
--- a/translations/sailpush_nb.ts
+++ b/translations/sailpush_nb.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Lagrede påloggingsopplysninger kunne ikke dekrypteres. Dette kan skje etter en systemoppdatering. Vennligst logg inn på nytt.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets-tjenesten er ikke tilgjengelig. Start enheten på nytt og prøv igjen.
+
LoginPage
diff --git a/translations/sailpush_nl.ts b/translations/sailpush_nl.ts
index 5aec927..3fd58c6 100644
--- a/translations/sailpush_nl.ts
+++ b/translations/sailpush_nl.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Opgeslagen inloggegevens konden niet worden ontsleuteld. Dit kan gebeuren na een systeemupdate. Log opnieuw in.
+
+ Secrets service is not available. Please restart the device and try again.
+ De secrets-service is niet beschikbaar. Start het apparaat opnieuw op en probeer het opnieuw.
+
LoginPage
diff --git a/translations/sailpush_pl.ts b/translations/sailpush_pl.ts
index 68afe30..649c92c 100644
--- a/translations/sailpush_pl.ts
+++ b/translations/sailpush_pl.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Zapisane dane logowania nie mogły zostać odszyfrowane. Może to nastąpić po aktualizacji systemu. Zaloguj się ponownie.
+
+ Secrets service is not available. Please restart the device and try again.
+ Usługa secrets jest niedostępna. Uruchom ponownie urządzenie i spróbuj ponownie.
+
LoginPage
diff --git a/translations/sailpush_pt.ts b/translations/sailpush_pt.ts
index e6ccc77..ca4f459 100644
--- a/translations/sailpush_pt.ts
+++ b/translations/sailpush_pt.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
As credenciais guardadas não puderam ser desencriptadas. Isto pode acontecer após uma atualização do sistema. Inicie sessão novamente.
+
+ Secrets service is not available. Please restart the device and try again.
+ O serviço de segredos não está disponível. Reinicie o dispositivo e tente novamente.
+
LoginPage
diff --git a/translations/sailpush_ro.ts b/translations/sailpush_ro.ts
index cfca53b..8c1e2a2 100644
--- a/translations/sailpush_ro.ts
+++ b/translations/sailpush_ro.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Datele de conectare salvate nu au putut fi decriptate. Acest lucru se poate întâmpla după o actualizare de sistem. Vă rugăm să vă autentificați din nou.
+
+ Secrets service is not available. Please restart the device and try again.
+ Serviciul secrets nu este disponibil. Reporniți dispozitivul și încercați din nou.
+
LoginPage
diff --git a/translations/sailpush_ru.ts b/translations/sailpush_ru.ts
index 66bd5eb..a52fbfa 100644
--- a/translations/sailpush_ru.ts
+++ b/translations/sailpush_ru.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Сохранённые учётные данные не удалось расшифровать. Это может произойти после обновления системы. Пожалуйста, войдите снова.
+
+ Secrets service is not available. Please restart the device and try again.
+ Служба secrets недоступна. Перезагрузите устройство и попробуйте снова.
+
LoginPage
diff --git a/translations/sailpush_sk.ts b/translations/sailpush_sk.ts
index f56a8e9..b60d18d 100644
--- a/translations/sailpush_sk.ts
+++ b/translations/sailpush_sk.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Uložené prihlasovacie údaje nemožno dešifrovať. K tomu môže dôjsť po aktualizácii systému. Prihláste sa znova.
+
+ Secrets service is not available. Please restart the device and try again.
+ Služba secrets nie je dostupná. Reštartujte zariadenie a skúste to znova.
+
LoginPage
diff --git a/translations/sailpush_sl.ts b/translations/sailpush_sl.ts
index 5caa00e..4ff45d3 100644
--- a/translations/sailpush_sl.ts
+++ b/translations/sailpush_sl.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Shranjenih poverilnic ni mogoče dešifrirati. To se lahko zgodi po posodobitvi sistema. Ponovno se prijavite.
+
+ Secrets service is not available. Please restart the device and try again.
+ Storitev secrets ni na voljo. Znova zaženite napravo in poskusite znova.
+
LoginPage
diff --git a/translations/sailpush_sq.ts b/translations/sailpush_sq.ts
index 01049e2..86a88dc 100644
--- a/translations/sailpush_sq.ts
+++ b/translations/sailpush_sq.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Kredencialet e ruajtura nuk mund të deshifroheshin. Kjo mund të ndodhë pas një përditësimi të sistemit. Ju lutemi, hyni përsëri.
+
+ Secrets service is not available. Please restart the device and try again.
+ Shërbimi i secrets nuk është i disponueshëm. Rinisni pajisjen dhe provoni përsëri.
+
LoginPage
diff --git a/translations/sailpush_sr.ts b/translations/sailpush_sr.ts
index a631125..94676fc 100644
--- a/translations/sailpush_sr.ts
+++ b/translations/sailpush_sr.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Sačuvani akreditivi ne mogu se dešifrovati. To se može dogoditi nakon ažuriranja sistema. Prijavite se ponovo.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets сервис није доступан. Поново покрените уређај и покушајте поново.
+
LoginPage
diff --git a/translations/sailpush_sv.ts b/translations/sailpush_sv.ts
index 8fad0d6..3e4f180 100644
--- a/translations/sailpush_sv.ts
+++ b/translations/sailpush_sv.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Sparade inloggningsuppgifter kunde inte dekrypteras. Detta kan hända efter en systemuppdatering. Vänligen logga in igen.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets-tjänsten är inte tillgänglig. Starta om enheten och försök igen.
+
LoginPage
diff --git a/translations/sailpush_tr.ts b/translations/sailpush_tr.ts
index a1ecaaa..ee6e6a8 100644
--- a/translations/sailpush_tr.ts
+++ b/translations/sailpush_tr.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Kayıtlı kimlik bilgileri çözülemedi. Bu durum bir sistem güncellemesinden sonra olabilir. Lütfen tekrar giriş yapın.
+
+ Secrets service is not available. Please restart the device and try again.
+ Secrets servisi kullanılamıyor. Lütfen cihazı yeniden başlatın ve tekrar deneyin.
+
LoginPage
diff --git a/translations/sailpush_uk.ts b/translations/sailpush_uk.ts
index 5cd6b1d..d4a3e5c 100644
--- a/translations/sailpush_uk.ts
+++ b/translations/sailpush_uk.ts
@@ -62,6 +62,10 @@
Saved credentials could not be decrypted. This can happen after a system update. Please log in again.
Збережені облікові дані не вдалося розшифрувати. Це може статися після оновлення системи. Будь ласка, увійдіть знову.
+
+ Secrets service is not available. Please restart the device and try again.
+ Служба secrets недоступна. Перезавантажте пристрій і спробуйте знову.
+
LoginPage