summaryrefslogtreecommitdiff
path: root/buteo-plugins
diff options
context:
space:
mode:
authorAndrew Branson <andrew.branson@jolla.com>2026-02-10 23:16:37 +0100
committerAndrew Branson <andrew.branson@jolla.com>2026-02-10 23:16:37 +0100
commit69628390815254297bbd8c95436f6780fa846fae (patch)
treedf6114043e489bf5d767ac39f0d20636e12cf3a2 /buteo-plugins
parentff1c2efe40bf53c146b4a2e3b5046ae8ecb32264 (diff)
Translations fixed and other stuff
Diffstat (limited to 'buteo-plugins')
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.cpp91
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.h1
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp256
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.h37
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp91
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.h1
6 files changed, 150 insertions, 327 deletions
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.cpp
index f915507..295d7b9 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.cpp
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.cpp
@@ -19,10 +19,10 @@
****************************************************************************/
#include "mastodondatatypesyncadaptor.h"
+#include "mastodonauthutils.h"
#include "trace.h"
#include <QtCore/QVariantMap>
-#include <QtCore/QUrl>
#include <QtNetwork/QNetworkRequest>
// libaccounts-qt5
@@ -131,29 +131,6 @@ void MastodonNotificationsDataTypeSyncAdaptor::setCredentialsNeedUpdate(Accounts
account->syncAndBlock();
}
-QString MastodonNotificationsDataTypeSyncAdaptor::normalizeApiHost(const QString &rawHost)
-{
- QString host = rawHost.trimmed();
- if (host.isEmpty()) {
- host = QStringLiteral("https://mastodon.social");
- }
- if (!host.startsWith(QLatin1String("https://"))
- && !host.startsWith(QLatin1String("http://"))) {
- host.prepend(QStringLiteral("https://"));
- }
-
- QUrl url(host);
- if (!url.isValid() || url.host().isEmpty()) {
- return QStringLiteral("https://mastodon.social");
- }
-
- QString normalized = QString::fromLatin1(url.toEncoded(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment));
- if (normalized.endsWith(QLatin1Char('/'))) {
- normalized.chop(1);
- }
- return normalized;
-}
-
void MastodonNotificationsDataTypeSyncAdaptor::signIn(Accounts::Account *account)
{
const int accountId = account->id();
@@ -185,59 +162,7 @@ void MastodonNotificationsDataTypeSyncAdaptor::signIn(Accounts::Account *account
}
QVariantMap signonSessionData = accSrv.authData().parameters();
- QString configuredHost = account->value(QStringLiteral("auth/oauth2/web_server/Host")).toString().trimmed();
- if (configuredHost.isEmpty()) {
- configuredHost = normalizeApiHost(account->value(QStringLiteral("api/Host")).toString());
- }
- if (configuredHost.startsWith(QLatin1String("https://"))) {
- configuredHost.remove(0, 8);
- } else if (configuredHost.startsWith(QLatin1String("http://"))) {
- configuredHost.remove(0, 7);
- }
- while (configuredHost.endsWith(QLatin1Char('/'))) {
- configuredHost.chop(1);
- }
- if (configuredHost.isEmpty()) {
- configuredHost = QStringLiteral("mastodon.social");
- }
- signonSessionData.insert(QStringLiteral("Host"), configuredHost);
-
- const QString authPath = account->value(QStringLiteral("auth/oauth2/web_server/AuthPath")).toString().trimmed();
- if (!authPath.isEmpty()) {
- signonSessionData.insert(QStringLiteral("AuthPath"), authPath);
- }
-
- const QString tokenPath = account->value(QStringLiteral("auth/oauth2/web_server/TokenPath")).toString().trimmed();
- if (!tokenPath.isEmpty()) {
- signonSessionData.insert(QStringLiteral("TokenPath"), tokenPath);
- }
-
- const QString responseType = account->value(QStringLiteral("auth/oauth2/web_server/ResponseType")).toString().trimmed();
- if (!responseType.isEmpty()) {
- signonSessionData.insert(QStringLiteral("ResponseType"), responseType);
- }
-
- const QString redirectUri = account->value(QStringLiteral("auth/oauth2/web_server/RedirectUri")).toString().trimmed();
- if (!redirectUri.isEmpty()) {
- signonSessionData.insert(QStringLiteral("RedirectUri"), redirectUri);
- }
-
- const QVariant scopeValue = account->value(QStringLiteral("auth/oauth2/web_server/Scope"));
- if (scopeValue.isValid()) {
- signonSessionData.insert(QStringLiteral("Scope"), scopeValue);
- }
-
- const QString clientId = account->value(QStringLiteral("auth/oauth2/web_server/ClientId")).toString().trimmed();
- if (!clientId.isEmpty()) {
- signonSessionData.insert(QStringLiteral("ClientId"), clientId);
- }
-
- const QString clientSecret = account->value(QStringLiteral("auth/oauth2/web_server/ClientSecret")).toString().trimmed();
- if (!clientSecret.isEmpty()) {
- signonSessionData.insert(QStringLiteral("ClientSecret"), clientSecret);
- }
-
- signonSessionData.insert(QStringLiteral("UiPolicy"), SignOn::NoUserInteractionPolicy);
+ MastodonAuthUtils::addSignOnSessionParameters(account, &signonSessionData);
connect(session, SIGNAL(response(SignOn::SessionData)),
this, SLOT(signOnResponse(SignOn::SessionData)),
@@ -276,10 +201,7 @@ void MastodonNotificationsDataTypeSyncAdaptor::signOnError(const SignOn::Error &
void MastodonNotificationsDataTypeSyncAdaptor::signOnResponse(const SignOn::SessionData &responseData)
{
- QVariantMap data;
- foreach (const QString &key, responseData.propertyNames()) {
- data.insert(key, responseData.getProperty(key));
- }
+ const QVariantMap data = MastodonAuthUtils::responseDataToMap(responseData);
QString accessToken;
SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession*>(sender());
@@ -287,16 +209,13 @@ void MastodonNotificationsDataTypeSyncAdaptor::signOnResponse(const SignOn::Sess
SignOn::Identity *identity = session->property("identity").value<SignOn::Identity*>();
const int accountId = account->id();
- accessToken = data.value(QLatin1String("AccessToken")).toString().trimmed();
- if (accessToken.isEmpty()) {
- accessToken = data.value(QLatin1String("access_token")).toString().trimmed();
- }
+ accessToken = MastodonAuthUtils::accessToken(data);
if (accessToken.isEmpty()) {
qCWarning(lcSocialPlugin) << "signon response for account with id" << accountId
<< "contained no access token; keys:" << data.keys();
}
- m_apiHosts.insert(accountId, normalizeApiHost(account->value(QStringLiteral("api/Host")).toString()));
+ m_apiHosts.insert(accountId, MastodonAuthUtils::normalizeApiHost(account->value(QStringLiteral("api/Host")).toString()));
session->disconnect(this);
identity->destroySession(session);
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.h b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.h
index 1c2d13f..3bb6e23 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.h
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodondatatypesyncadaptor.h
@@ -59,7 +59,6 @@ private Q_SLOTS:
void signOnResponse(const SignOn::SessionData &responseData);
private:
- static QString normalizeApiHost(const QString &rawHost);
void setCredentialsNeedUpdate(Accounts::Account *account);
void signIn(Accounts::Account *account);
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp
index aa1089c..79b996c 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp
@@ -31,6 +31,8 @@
#include <notification.h>
+#include <algorithm>
+
#define OPEN_BROWSER_ACTION(openUrlArgs) \
Notification::remoteAction( \
"default", \
@@ -45,8 +47,9 @@
namespace {
const char *const NotificationCategory = "x-nemo.social.mastodon.notification";
const char *const LastReadIdProperty = "mastodonLastReadId";
+ const char *const NotificationIdHint = "x-nemo.sociald.notification-id";
const int NotificationsPageLimit = 80;
- const uint NotificationDismissedReason = 2;
+ const uint NotificationDismissedReason = 1;
QString decodeHtmlEntities(QString text)
{
@@ -118,41 +121,12 @@ namespace {
return QStringLiteral("sent you a notification");
}
- QList<QPair<QString, SocialPostImage::ImageType> > parseMediaAttachments(const QJsonObject &statusObject)
- {
- QList<QPair<QString, SocialPostImage::ImageType> > imageList;
-
- const QJsonArray mediaAttachments = statusObject.value(QStringLiteral("media_attachments")).toArray();
- foreach (const QJsonValue &attachmentValue, mediaAttachments) {
- const QJsonObject attachment = attachmentValue.toObject();
- const QString mediaType = attachment.value(QStringLiteral("type")).toString();
-
- QString mediaUrl;
- SocialPostImage::ImageType imageType = SocialPostImage::Invalid;
- if (mediaType == QLatin1String("image")) {
- mediaUrl = attachment.value(QStringLiteral("url")).toString();
- imageType = SocialPostImage::Photo;
- } else if (mediaType == QLatin1String("video") || mediaType == QLatin1String("gifv")) {
- mediaUrl = attachment.value(QStringLiteral("preview_url")).toString();
- if (mediaUrl.isEmpty()) {
- mediaUrl = attachment.value(QStringLiteral("url")).toString();
- }
- imageType = SocialPostImage::Video;
- }
-
- if (!mediaUrl.isEmpty() && imageType != SocialPostImage::Invalid) {
- imageList.append(qMakePair(mediaUrl, imageType));
- }
- }
-
- return imageList;
- }
}
MastodonNotificationsSyncAdaptor::MastodonNotificationsSyncAdaptor(QObject *parent)
: MastodonNotificationsDataTypeSyncAdaptor(SocialNetworkSyncAdaptor::Notifications, parent)
{
- setInitialActive(m_db.isValid());
+ setInitialActive(true);
}
MastodonNotificationsSyncAdaptor::~MastodonNotificationsSyncAdaptor()
@@ -166,17 +140,7 @@ QString MastodonNotificationsSyncAdaptor::syncServiceName() const
void MastodonNotificationsSyncAdaptor::purgeDataForOldAccount(int oldId, SocialNetworkSyncAdaptor::PurgeMode)
{
- Notification *notification = findNotification(oldId);
- if (notification) {
- notification->close();
- notification->deleteLater();
- }
-
- m_db.removePosts(oldId);
- m_db.commit();
- m_db.wait();
-
- purgeCachedImages(&m_imageCacheDb, oldId);
+ closeAccountNotifications(oldId);
m_pendingSyncStates.remove(oldId);
m_accessTokens.remove(oldId);
@@ -193,12 +157,10 @@ void MastodonNotificationsSyncAdaptor::beginSync(int accountId, const QString &a
void MastodonNotificationsSyncAdaptor::finalize(int accountId)
{
if (syncAborted()) {
- qCInfo(lcSocialPlugin) << "sync aborted, won't commit database changes";
- } else {
- m_db.commit();
- m_db.wait();
- purgeExpiredImages(&m_imageCacheDb, accountId);
+ qCInfo(lcSocialPlugin) << "sync aborted, won't update notifications";
}
+
+ Q_UNUSED(accountId)
}
QString MastodonNotificationsSyncAdaptor::sanitizeContent(const QString &content)
@@ -271,6 +233,11 @@ int MastodonNotificationsSyncAdaptor::compareNotificationIds(const QString &left
return left < right ? -1 : 1;
}
+QString MastodonNotificationsSyncAdaptor::notificationObjectKey(int accountId, const QString &notificationId)
+{
+ return QString::number(accountId) + QLatin1Char(':') + notificationId;
+}
+
void MastodonNotificationsSyncAdaptor::requestUnreadMarker(int accountId, const QString &accessToken)
{
QUrl url(apiHost(accountId) + QStringLiteral("/api/v1/markers"));
@@ -369,7 +336,6 @@ void MastodonNotificationsSyncAdaptor::finishedUnreadMarkerHandler()
PendingSyncState state;
state.accessToken = accessToken;
state.minReadId = minReadId;
- state.maxNotificationId = minReadId;
m_pendingSyncStates.insert(accountId, state);
requestNotifications(accountId, accessToken, minReadId);
@@ -397,33 +363,19 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler()
if (state.accessToken.isEmpty()) {
state.accessToken = accessToken;
state.minReadId = minId;
- state.maxNotificationId = minId;
}
bool ok = false;
const QJsonArray notifications = parseJsonArrayReplyData(replyData, &ok);
if (!isError && ok) {
if (!notifications.size()) {
- if (!state.dbCleared) {
- m_db.removePosts(accountId);
- state.dbCleared = true;
- }
- Notification *notification = findNotification(accountId);
- if (notification) {
- notification->close();
- notification->deleteLater();
- }
+ closeAccountNotifications(accountId);
qCDebug(lcSocialPlugin) << "no notifications received for account" << accountId;
m_pendingSyncStates.remove(accountId);
decrementSemaphore(accountId);
return;
}
- if (!state.dbCleared) {
- m_db.removePosts(accountId);
- state.dbCleared = true;
- }
-
QString pageMinNotificationId;
foreach (const QJsonValue &notificationValue, notifications) {
@@ -441,10 +393,6 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler()
|| compareNotificationIds(notificationId, pageMinNotificationId) < 0) {
pageMinNotificationId = notificationId;
}
- if (state.maxNotificationId.isEmpty()
- || compareNotificationIds(notificationId, state.maxNotificationId) > 0) {
- state.maxNotificationId = notificationId;
- }
const QString notificationType = notificationObject.value(QStringLiteral("type")).toString();
const QJsonObject actorObject = notificationObject.value(QStringLiteral("account")).toObject();
@@ -464,11 +412,6 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler()
const QString displayName = displayNameForAccount(actorObject);
const QString accountName = actorObject.value(QStringLiteral("acct")).toString();
- QString icon = actorObject.value(QStringLiteral("avatar_static")).toString();
- if (icon.isEmpty()) {
- icon = actorObject.value(QStringLiteral("avatar")).toString();
- }
-
const QString statusBody = sanitizeContent(statusObject.value(QStringLiteral("content")).toString());
const QString action = actionText(notificationType);
QString body;
@@ -495,33 +438,13 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler()
url = QStringLiteral("%1/@%2").arg(apiHost(accountId), accountName);
}
- QString boostedBy;
- if (notificationType == QLatin1String("reblog")
- || notificationType == QLatin1String("favourite")) {
- boostedBy = displayName;
- }
-
- const QList<QPair<QString, SocialPostImage::ImageType> > imageList = parseMediaAttachments(statusObject);
-
- m_db.addMastodonNotification(QStringLiteral("n:%1").arg(notificationId),
- displayName,
- accountName,
- body,
- eventTimestamp,
- icon,
- imageList,
- url,
- boostedBy,
- apiHost(accountId),
- accountId);
-
- ++state.newNotificationCount;
- if (state.newNotificationCount == 1) {
- state.singleSummary = displayName;
- state.singleBody = body;
- state.singleLink = url;
- state.singleTimestamp = eventTimestamp;
- }
+ PendingNotification pendingNotification;
+ pendingNotification.notificationId = notificationId;
+ pendingNotification.summary = displayName;
+ pendingNotification.body = body;
+ pendingNotification.link = url;
+ pendingNotification.timestamp = eventTimestamp;
+ state.pendingNotifications.insert(notificationId, pendingNotification);
}
if (notifications.size() >= NotificationsPageLimit
@@ -533,8 +456,19 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler()
return;
}
- if (state.newNotificationCount > 0) {
- publishSystemNotification(accountId, state);
+ if (state.pendingNotifications.size() > 0) {
+ QStringList notificationIds = state.pendingNotifications.keys();
+ std::sort(notificationIds.begin(), notificationIds.end(), [](const QString &left, const QString &right) {
+ return compareNotificationIds(left, right) > 0;
+ });
+
+ QSet<QString> keepNotificationIds;
+ foreach (const QString &notificationId, notificationIds) {
+ const PendingNotification pendingNotification = state.pendingNotifications.value(notificationId);
+ publishSystemNotification(accountId, pendingNotification);
+ keepNotificationIds.insert(notificationId);
+ }
+ closeAccountNotifications(accountId, keepNotificationIds);
}
} else {
qCWarning(lcSocialPlugin) << "unable to parse notifications data from request with account" << accountId
@@ -601,30 +535,76 @@ void MastodonNotificationsSyncAdaptor::finishedMarkReadHandler()
decrementSemaphore(accountId);
}
-void MastodonNotificationsSyncAdaptor::publishSystemNotification(int accountId, const PendingSyncState &state)
+void MastodonNotificationsSyncAdaptor::publishSystemNotification(int accountId,
+ const PendingNotification &notificationData)
{
- Notification *notification = createNotification(accountId);
- notification->setItemCount(state.newNotificationCount);
-
- QStringList openUrlArgs;
- if (notification->itemCount() == 1) {
- notification->setTimestamp(state.singleTimestamp.isValid() ? state.singleTimestamp : QDateTime::currentDateTimeUtc());
- notification->setSummary(state.singleSummary.isEmpty() ? QStringLiteral("Mastodon") : state.singleSummary);
- notification->setBody(state.singleBody.isEmpty() ? QStringLiteral("New notification") : state.singleBody);
- openUrlArgs << (state.singleLink.isEmpty() ? apiHost(accountId) + QStringLiteral("/notifications") : state.singleLink);
- } else {
- notification->setTimestamp(QDateTime::currentDateTimeUtc());
- notification->setSummary(QStringLiteral("Mastodon"));
- notification->setBody(QStringLiteral("You have %1 new notifications").arg(notification->itemCount()));
- openUrlArgs << apiHost(accountId) + QStringLiteral("/notifications");
- }
-
- notification->setProperty(LastReadIdProperty, state.maxNotificationId);
+ Notification *notification = createNotification(accountId, notificationData.notificationId);
+ notification->setItemCount(1);
+ notification->setTimestamp(notificationData.timestamp.isValid()
+ ? notificationData.timestamp
+ : QDateTime::currentDateTimeUtc());
+ notification->setSummary(notificationData.summary.isEmpty()
+ ? QStringLiteral("Mastodon")
+ : notificationData.summary);
+ notification->setBody(notificationData.body.isEmpty()
+ ? QStringLiteral("New notification")
+ : notificationData.body);
+ notification->setPreviewSummary(notificationData.summary);
+ notification->setPreviewBody(notificationData.body);
+
+ const QString openUrl = notificationData.link.isEmpty()
+ ? apiHost(accountId) + QStringLiteral("/notifications")
+ : notificationData.link;
+ notification->setProperty(LastReadIdProperty, notificationData.notificationId);
notification->setUrgency(Notification::Low);
- notification->setRemoteAction(OPEN_BROWSER_ACTION(openUrlArgs));
+ notification->setRemoteAction(OPEN_BROWSER_ACTION(QStringList() << openUrl));
notification->publish();
if (notification->replacesId() == 0) {
- qCWarning(lcSocialPlugin) << "failed to publish Mastodon notification";
+ qCWarning(lcSocialPlugin) << "failed to publish Mastodon notification"
+ << notificationData.notificationId;
+ }
+}
+
+void MastodonNotificationsSyncAdaptor::closeAccountNotifications(int accountId,
+ const QSet<QString> &keepNotificationIds)
+{
+ QStringList cachedKeys = m_notificationObjects.keys();
+ foreach (const QString &objectKey, cachedKeys) {
+ Notification *notification = m_notificationObjects.value(objectKey);
+ if (!notification
+ || notification->hintValue("x-nemo.sociald.account-id").toInt() != accountId) {
+ continue;
+ }
+
+ const QString notificationId = notification->hintValue(NotificationIdHint).toString();
+ if (!notificationId.isEmpty() && keepNotificationIds.contains(notificationId)) {
+ continue;
+ }
+
+ notification->close();
+ m_notificationObjects.remove(objectKey);
+ notification->deleteLater();
+ }
+
+ QList<QObject *> notifications = Notification::notifications();
+ foreach (QObject *object, notifications) {
+ Notification *notification = qobject_cast<Notification *>(object);
+ if (!notification) {
+ delete object;
+ continue;
+ }
+
+ if (notification->category() == QLatin1String(NotificationCategory)
+ && notification->hintValue("x-nemo.sociald.account-id").toInt() == accountId) {
+ const QString notificationId = notification->hintValue(NotificationIdHint).toString();
+ if (notificationId.isEmpty() || !keepNotificationIds.contains(notificationId)) {
+ notification->close();
+ }
+ }
+
+ if (notification->parent() != this) {
+ delete notification;
+ }
}
}
@@ -662,30 +642,42 @@ void MastodonNotificationsSyncAdaptor::markReadFromNotification(Notification *no
requestMarkRead(accountId, accessToken, lastReadId);
}
-Notification *MastodonNotificationsSyncAdaptor::createNotification(int accountId)
+Notification *MastodonNotificationsSyncAdaptor::createNotification(int accountId, const QString &notificationId)
{
- Notification *notification = findNotification(accountId);
+ const QString objectKey = notificationObjectKey(accountId, notificationId);
+ Notification *notification = m_notificationObjects.value(objectKey);
+ if (!notification) {
+ notification = findNotification(accountId, notificationId);
+ }
if (!notification) {
notification = new Notification(this);
- notification->setAppName(QStringLiteral("Mastodon"));
- notification->setAppIcon(QStringLiteral("icon-l-mastodon"));
- notification->setHintValue("x-nemo.sociald.account-id", accountId);
- notification->setHintValue("x-nemo-feedback", QStringLiteral("social"));
- notification->setCategory(QLatin1String(NotificationCategory));
+ } else if (notification->parent() != this) {
+ notification->setParent(this);
}
+
+ notification->setAppName(QStringLiteral("Mastodon"));
+ notification->setAppIcon(QStringLiteral("icon-l-mastodon"));
+ notification->setHintValue("x-nemo.sociald.account-id", accountId);
+ notification->setHintValue(NotificationIdHint, notificationId);
+ notification->setHintValue("x-nemo-feedback", QStringLiteral("social"));
+ notification->setCategory(QLatin1String(NotificationCategory));
+
connect(notification, SIGNAL(closed(uint)), this, SLOT(notificationClosedWithReason(uint)), Qt::UniqueConnection);
+ m_notificationObjects.insert(objectKey, notification);
return notification;
}
-Notification *MastodonNotificationsSyncAdaptor::findNotification(int accountId)
+Notification *MastodonNotificationsSyncAdaptor::findNotification(int accountId, const QString &notificationId)
{
Notification *notification = 0;
QList<QObject *> notifications = Notification::notifications();
foreach (QObject *object, notifications) {
- Notification *castedNotification = static_cast<Notification *>(object);
- if (castedNotification->category() == QLatin1String(NotificationCategory)
- && castedNotification->hintValue("x-nemo.sociald.account-id").toInt() == accountId) {
+ Notification *castedNotification = qobject_cast<Notification *>(object);
+ if (castedNotification
+ && castedNotification->category() == QLatin1String(NotificationCategory)
+ && castedNotification->hintValue("x-nemo.sociald.account-id").toInt() == accountId
+ && castedNotification->hintValue(NotificationIdHint).toString() == notificationId) {
notification = castedNotification;
break;
}
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.h b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.h
index 8c79d7d..0cd63a4 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.h
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.h
@@ -25,11 +25,9 @@
#include <QtCore/QDateTime>
#include <QtCore/QHash>
+#include <QtCore/QSet>
#include <QtNetwork/QNetworkReply>
-#include "mastodonnotificationsdatabase.h"
-#include <socialcache/socialimagesdatabase.h>
-
class Notification;
class MastodonNotificationsSyncAdaptor : public MastodonNotificationsDataTypeSyncAdaptor
@@ -48,22 +46,18 @@ protected:
void finalize(int accountId) override;
private:
+ struct PendingNotification {
+ QString notificationId;
+ QString summary;
+ QString body;
+ QString link;
+ QDateTime timestamp;
+ };
+
struct PendingSyncState {
QString accessToken;
QString minReadId;
- QString maxNotificationId;
- int newNotificationCount;
- QString singleSummary;
- QString singleBody;
- QString singleLink;
- QDateTime singleTimestamp;
- bool dbCleared;
-
- PendingSyncState()
- : newNotificationCount(0)
- , dbCleared(false)
- {
- }
+ QHash<QString, PendingNotification> pendingNotifications;
};
static QString sanitizeContent(const QString &content);
@@ -76,9 +70,11 @@ private:
const QString &minId,
const QString &maxId = QString());
void requestMarkRead(int accountId, const QString &accessToken, const QString &lastReadId);
- void publishSystemNotification(int accountId, const PendingSyncState &state);
- Notification *createNotification(int accountId);
- Notification *findNotification(int accountId);
+ void publishSystemNotification(int accountId, const PendingNotification &notificationData);
+ Notification *createNotification(int accountId, const QString &notificationId);
+ Notification *findNotification(int accountId, const QString &notificationId);
+ void closeAccountNotifications(int accountId, const QSet<QString> &keepNotificationIds = QSet<QString>());
+ static QString notificationObjectKey(int accountId, const QString &notificationId);
void markReadFromNotification(Notification *notification);
private Q_SLOTS:
@@ -88,11 +84,10 @@ private Q_SLOTS:
void notificationClosedWithReason(uint reason);
private:
- MastodonNotificationsDatabase m_db;
- SocialImagesDatabase m_imageCacheDb;
QHash<int, PendingSyncState> m_pendingSyncStates;
QHash<int, QString> m_accessTokens;
QHash<int, QString> m_lastMarkedReadIds;
+ QHash<QString, Notification *> m_notificationObjects;
};
#endif // MASTODONNOTIFICATIONSSYNCADAPTOR_H
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp
index 83a5249..7b47fe8 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp
@@ -19,10 +19,10 @@
****************************************************************************/
#include "mastodondatatypesyncadaptor.h"
+#include "mastodonauthutils.h"
#include "trace.h"
#include <QtCore/QVariantMap>
-#include <QtCore/QUrl>
#include <QtNetwork/QNetworkRequest>
// libaccounts-qt5
@@ -131,29 +131,6 @@ void MastodonDataTypeSyncAdaptor::setCredentialsNeedUpdate(Accounts::Account *ac
account->syncAndBlock();
}
-QString MastodonDataTypeSyncAdaptor::normalizeApiHost(const QString &rawHost)
-{
- QString host = rawHost.trimmed();
- if (host.isEmpty()) {
- host = QStringLiteral("https://mastodon.social");
- }
- if (!host.startsWith(QLatin1String("https://"))
- && !host.startsWith(QLatin1String("http://"))) {
- host.prepend(QStringLiteral("https://"));
- }
-
- QUrl url(host);
- if (!url.isValid() || url.host().isEmpty()) {
- return QStringLiteral("https://mastodon.social");
- }
-
- QString normalized = QString::fromLatin1(url.toEncoded(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment));
- if (normalized.endsWith(QLatin1Char('/'))) {
- normalized.chop(1);
- }
- return normalized;
-}
-
void MastodonDataTypeSyncAdaptor::signIn(Accounts::Account *account)
{
const int accountId = account->id();
@@ -185,59 +162,7 @@ void MastodonDataTypeSyncAdaptor::signIn(Accounts::Account *account)
}
QVariantMap signonSessionData = accSrv.authData().parameters();
- QString configuredHost = account->value(QStringLiteral("auth/oauth2/web_server/Host")).toString().trimmed();
- if (configuredHost.isEmpty()) {
- configuredHost = normalizeApiHost(account->value(QStringLiteral("api/Host")).toString());
- }
- if (configuredHost.startsWith(QLatin1String("https://"))) {
- configuredHost.remove(0, 8);
- } else if (configuredHost.startsWith(QLatin1String("http://"))) {
- configuredHost.remove(0, 7);
- }
- while (configuredHost.endsWith(QLatin1Char('/'))) {
- configuredHost.chop(1);
- }
- if (configuredHost.isEmpty()) {
- configuredHost = QStringLiteral("mastodon.social");
- }
- signonSessionData.insert(QStringLiteral("Host"), configuredHost);
-
- const QString authPath = account->value(QStringLiteral("auth/oauth2/web_server/AuthPath")).toString().trimmed();
- if (!authPath.isEmpty()) {
- signonSessionData.insert(QStringLiteral("AuthPath"), authPath);
- }
-
- const QString tokenPath = account->value(QStringLiteral("auth/oauth2/web_server/TokenPath")).toString().trimmed();
- if (!tokenPath.isEmpty()) {
- signonSessionData.insert(QStringLiteral("TokenPath"), tokenPath);
- }
-
- const QString responseType = account->value(QStringLiteral("auth/oauth2/web_server/ResponseType")).toString().trimmed();
- if (!responseType.isEmpty()) {
- signonSessionData.insert(QStringLiteral("ResponseType"), responseType);
- }
-
- const QString redirectUri = account->value(QStringLiteral("auth/oauth2/web_server/RedirectUri")).toString().trimmed();
- if (!redirectUri.isEmpty()) {
- signonSessionData.insert(QStringLiteral("RedirectUri"), redirectUri);
- }
-
- const QVariant scopeValue = account->value(QStringLiteral("auth/oauth2/web_server/Scope"));
- if (scopeValue.isValid()) {
- signonSessionData.insert(QStringLiteral("Scope"), scopeValue);
- }
-
- const QString clientId = account->value(QStringLiteral("auth/oauth2/web_server/ClientId")).toString().trimmed();
- if (!clientId.isEmpty()) {
- signonSessionData.insert(QStringLiteral("ClientId"), clientId);
- }
-
- const QString clientSecret = account->value(QStringLiteral("auth/oauth2/web_server/ClientSecret")).toString().trimmed();
- if (!clientSecret.isEmpty()) {
- signonSessionData.insert(QStringLiteral("ClientSecret"), clientSecret);
- }
-
- signonSessionData.insert(QStringLiteral("UiPolicy"), SignOn::NoUserInteractionPolicy);
+ MastodonAuthUtils::addSignOnSessionParameters(account, &signonSessionData);
connect(session, SIGNAL(response(SignOn::SessionData)),
this, SLOT(signOnResponse(SignOn::SessionData)),
@@ -276,10 +201,7 @@ void MastodonDataTypeSyncAdaptor::signOnError(const SignOn::Error &error)
void MastodonDataTypeSyncAdaptor::signOnResponse(const SignOn::SessionData &responseData)
{
- QVariantMap data;
- foreach (const QString &key, responseData.propertyNames()) {
- data.insert(key, responseData.getProperty(key));
- }
+ const QVariantMap data = MastodonAuthUtils::responseDataToMap(responseData);
QString accessToken;
SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession*>(sender());
@@ -287,16 +209,13 @@ void MastodonDataTypeSyncAdaptor::signOnResponse(const SignOn::SessionData &resp
SignOn::Identity *identity = session->property("identity").value<SignOn::Identity*>();
const int accountId = account->id();
- accessToken = data.value(QLatin1String("AccessToken")).toString().trimmed();
- if (accessToken.isEmpty()) {
- accessToken = data.value(QLatin1String("access_token")).toString().trimmed();
- }
+ accessToken = MastodonAuthUtils::accessToken(data);
if (accessToken.isEmpty()) {
qCWarning(lcSocialPlugin) << "signon response for account with id" << accountId
<< "contained no access token; keys:" << data.keys();
}
- m_apiHosts.insert(accountId, normalizeApiHost(account->value(QStringLiteral("api/Host")).toString()));
+ m_apiHosts.insert(accountId, MastodonAuthUtils::normalizeApiHost(account->value(QStringLiteral("api/Host")).toString()));
session->disconnect(this);
identity->destroySession(session);
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.h b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.h
index ad8321d..3ebbbf5 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.h
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.h
@@ -59,7 +59,6 @@ private Q_SLOTS:
void signOnResponse(const SignOn::SessionData &responseData);
private:
- static QString normalizeApiHost(const QString &rawHost);
void setCredentialsNeedUpdate(Accounts::Account *account);
void signIn(Accounts::Account *account);