diff options
Diffstat (limited to 'buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp')
| -rw-r--r-- | buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp | 395 |
1 files changed, 290 insertions, 105 deletions
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp index 7775841..98dbbc8 100644 --- a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp +++ b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp @@ -19,12 +19,13 @@ ****************************************************************************/ #include "mastodonnotificationssyncadaptor.h" -#include "trace.h" +#include "mastodontextutils.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QLoggingCategory> #include <QtCore/QJsonArray> #include <QtCore/QJsonObject> #include <QtCore/QJsonValue> -#include <QtCore/QRegularExpression> #include <QtCore/QUrl> #include <QtCore/QUrlQuery> #include <QtNetwork/QNetworkRequest> @@ -50,42 +51,79 @@ ) namespace { + Q_LOGGING_CATEGORY(lcMastodonNotifications, "buteo.plugin.mastodon.notifications", QtWarningMsg) + const char *const NotificationCategory = "x-nemo.social.mastodon.notification"; const char *const NotificationIdHint = "x-nemo.sociald.notification-id"; const char *const LastFetchedNotificationIdKey = "LastFetchedNotificationId"; const int NotificationsPageLimit = 80; - - QString decodeHtmlEntities(QString text) - { - text.replace(QStringLiteral("""), QStringLiteral("\"")); - text.replace(QStringLiteral("'"), QStringLiteral("'")); - text.replace(QStringLiteral("<"), QStringLiteral("<")); - text.replace(QStringLiteral(">"), QStringLiteral(">")); - text.replace(QStringLiteral("&"), QStringLiteral("&")); - text.replace(QStringLiteral(" "), QStringLiteral(" ")); - - static const QRegularExpression decimalEntity(QStringLiteral("&#(\\d+);")); - QRegularExpressionMatch match; - int index = 0; - while ((index = text.indexOf(decimalEntity, index, &match)) != -1) { - const uint value = match.captured(1).toUInt(); - const QString replacement = value > 0 ? QString(QChar(value)) : QString(); - text.replace(index, match.capturedLength(0), replacement); - index += replacement.size(); - } - - static const QRegularExpression hexEntity(QStringLiteral("&#x([0-9a-fA-F]+);")); - index = 0; - while ((index = text.indexOf(hexEntity, index, &match)) != -1) { - bool ok = false; - const uint value = match.captured(1).toUInt(&ok, 16); - const QString replacement = ok && value > 0 ? QString(QChar(value)) : QString(); - text.replace(index, match.capturedLength(0), replacement); - index += replacement.size(); - } - - return text; - } + const uint NotificationDismissedReason = 1; + + //% "mentioned you" + const char *const TrIdMentionedYou = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-mentioned_you"); + //% "boosted your post" + const char *const TrIdBoostedYourPost = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-boosted_your_post"); + //% "favourited your post" + const char *const TrIdFavouritedYourPost = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-favourited_your_post"); + //% "started following you" + const char *const TrIdStartedFollowingYou = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-started_following_you"); + //% "requested to follow you" + const char *const TrIdRequestedToFollowYou = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-requested_to_follow_you"); + //% "interacted with your poll" + const char *const TrIdInteractedWithYourPoll = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-interacted_with_your_poll"); + //% "posted" + const char *const TrIdPosted = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-posted"); + //% "updated a post" + const char *const TrIdUpdatedPost = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-updated_post"); + //% "signed up" + const char *const TrIdSignedUp = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-signed_up"); + //% "reported an account" + const char *const TrIdReportedAccount = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-reported_account"); + //% "received a moderation warning" + const char *const TrIdReceivedModerationWarning = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-received_moderation_warning"); + //% "quoted your post" + const char *const TrIdQuotedYourPost = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-quoted_your_post"); + //% "updated a post that quoted you" + const char *const TrIdUpdatedQuotedPost = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-updated_quoted_post"); + //% "sent you a notification" + const char *const TrIdSentNotification = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-sent_notification"); + + //% "An admin blocked an instance" + const char *const TrIdAdminBlockedInstance = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-admin_blocked_instance"); + //% "An admin blocked %1" + const char *const TrIdAdminBlockedTarget = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-admin_blocked_target"); + //% "You blocked an instance" + const char *const TrIdYouBlockedInstance = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-you_blocked_instance"); + //% "You blocked %1" + const char *const TrIdYouBlockedTarget = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-you_blocked_target"); + //% "An account was suspended" + const char *const TrIdAccountSuspended = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-account_suspended"); + //% "%1 was suspended" + const char *const TrIdTargetSuspended = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-target_suspended"); + //% "Some follow relationships were severed" + const char *const TrIdRelationshipsSevered = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-relationships_severed"); + //% "%1 (%2 followers, %3 following removed)" + const char *const TrIdRelationshipsSummary = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-relationships_summary"); + + //% "A moderator sent you a warning" + const char *const TrIdModeratorWarningNone = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-moderator_warning_none"); + //% "A moderator disabled your account" + const char *const TrIdModeratorWarningDisable = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-moderator_warning_disable"); + //% "A moderator marked specific posts as sensitive" + const char *const TrIdModeratorWarningSpecificSensitive = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-moderator_warning_specific_sensitive"); + //% "A moderator deleted specific posts" + const char *const TrIdModeratorWarningDeletePosts = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-moderator_warning_delete_posts"); + //% "A moderator marked all your posts as sensitive" + const char *const TrIdModeratorWarningAllSensitive = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-moderator_warning_all_sensitive"); + //% "A moderator limited your account" + const char *const TrIdModeratorWarningSilence = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-moderator_warning_silence"); + //% "A moderator suspended your account" + const char *const TrIdModeratorWarningSuspend = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-moderator_warning_suspend"); + + //% "Mastodon" + const char *const TrIdMastodon = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-mastodon"); + //% "New notification" + const char *const TrIdNewNotification = QT_TRID_NOOP("lipstick-jolla-home-la-mastodon-notification-new_notification"); QString displayNameForAccount(const QJsonObject &account) { @@ -105,33 +143,112 @@ namespace { QString actionText(const QString &type) { if (type == QLatin1String("mention")) { - return QStringLiteral("mentioned you"); + return qtTrId(TrIdMentionedYou); } else if (type == QLatin1String("reblog")) { - return QStringLiteral("boosted your post"); + return qtTrId(TrIdBoostedYourPost); } else if (type == QLatin1String("favourite")) { - return QStringLiteral("favourited your post"); + return qtTrId(TrIdFavouritedYourPost); } else if (type == QLatin1String("follow")) { - return QStringLiteral("started following you"); + return qtTrId(TrIdStartedFollowingYou); } else if (type == QLatin1String("follow_request")) { - return QStringLiteral("requested to follow you"); + return qtTrId(TrIdRequestedToFollowYou); } else if (type == QLatin1String("poll")) { - return QStringLiteral("interacted with your poll"); + return qtTrId(TrIdInteractedWithYourPoll); } else if (type == QLatin1String("status")) { - return QStringLiteral("posted"); + return qtTrId(TrIdPosted); } else if (type == QLatin1String("update")) { - return QStringLiteral("updated a post"); + return qtTrId(TrIdUpdatedPost); + } else if (type == QLatin1String("admin.sign_up")) { + return qtTrId(TrIdSignedUp); + } else if (type == QLatin1String("admin.report")) { + return qtTrId(TrIdReportedAccount); + } else if (type == QLatin1String("moderation_warning")) { + return qtTrId(TrIdReceivedModerationWarning); + } else if (type == QLatin1String("quote")) { + return qtTrId(TrIdQuotedYourPost); + } else if (type == QLatin1String("quoted_update")) { + return qtTrId(TrIdUpdatedQuotedPost); } - return QStringLiteral("sent you a notification"); + return qtTrId(TrIdSentNotification); + } + + bool useSystemSummary(const QString ¬ificationType) + { + return notificationType == QLatin1String("severed_relationships") + || notificationType == QLatin1String("moderation_warning"); } - bool hasActiveNotificationsForAccount(int accountId) + QString severedRelationshipsText(const QJsonObject &eventObject) + { + const QString eventType = eventObject.value(QStringLiteral("type")).toString(); + const QString targetName = eventObject.value(QStringLiteral("target_name")).toString().trimmed(); + const int followersCount = eventObject.value(QStringLiteral("followers_count")).toInt(); + const int followingCount = eventObject.value(QStringLiteral("following_count")).toInt(); + + QString action; + if (eventType == QLatin1String("domain_block")) { + action = targetName.isEmpty() + ? qtTrId(TrIdAdminBlockedInstance) + : qtTrId(TrIdAdminBlockedTarget).arg(targetName); + } else if (eventType == QLatin1String("user_domain_block")) { + action = targetName.isEmpty() + ? qtTrId(TrIdYouBlockedInstance) + : qtTrId(TrIdYouBlockedTarget).arg(targetName); + } else if (eventType == QLatin1String("account_suspension")) { + action = targetName.isEmpty() + ? qtTrId(TrIdAccountSuspended) + : qtTrId(TrIdTargetSuspended).arg(targetName); + } else { + action = qtTrId(TrIdRelationshipsSevered); + } + + const int affectedCount = followersCount + followingCount; + if (affectedCount <= 0) { + return action; + } + + return qtTrId(TrIdRelationshipsSummary) + .arg(action) + .arg(followersCount) + .arg(followingCount); + } + + QString moderationWarningText(const QJsonObject &warningObject) + { + const QString warningText = warningObject.value(QStringLiteral("text")).toString().trimmed(); + if (!warningText.isEmpty()) { + return warningText; + } + + const QString action = warningObject.value(QStringLiteral("action")).toString(); + if (action == QLatin1String("none")) { + return qtTrId(TrIdModeratorWarningNone); + } else if (action == QLatin1String("disable")) { + return qtTrId(TrIdModeratorWarningDisable); + } else if (action == QLatin1String("mark_statuses_as_sensitive")) { + return qtTrId(TrIdModeratorWarningSpecificSensitive); + } else if (action == QLatin1String("delete_statuses")) { + return qtTrId(TrIdModeratorWarningDeletePosts); + } else if (action == QLatin1String("sensitive")) { + return qtTrId(TrIdModeratorWarningAllSensitive); + } else if (action == QLatin1String("silence")) { + return qtTrId(TrIdModeratorWarningSilence); + } else if (action == QLatin1String("suspend")) { + return qtTrId(TrIdModeratorWarningSuspend); + } + + return QString(); + } + + bool hasActiveNotificationsForAccount(int accountId, const Notification *ignoredNotification = 0) { bool hasActiveNotifications = false; const QList<QObject *> notifications = Notification::notifications(); foreach (QObject *object, notifications) { Notification *notification = qobject_cast<Notification *>(object); if (notification + && notification != ignoredNotification && notification->category() == QLatin1String(NotificationCategory) && notification->hintValue("x-nemo.sociald.account-id").toInt() == accountId) { hasActiveNotifications = true; @@ -185,6 +302,11 @@ MastodonNotificationsSyncAdaptor::~MastodonNotificationsSyncAdaptor() QString MastodonNotificationsSyncAdaptor::syncServiceName() const { + return QStringLiteral("mastodon-notifications"); +} + +QString MastodonNotificationsSyncAdaptor::authServiceName() const +{ return QStringLiteral("mastodon-microblog"); } @@ -192,6 +314,7 @@ void MastodonNotificationsSyncAdaptor::purgeDataForOldAccount(int oldId, SocialN { closeAccountNotifications(oldId); + m_accessTokens.remove(oldId); m_pendingSyncStates.remove(oldId); m_lastMarkedReadIds.remove(oldId); saveLastFetchedId(oldId, QString()); @@ -199,6 +322,7 @@ void MastodonNotificationsSyncAdaptor::purgeDataForOldAccount(int oldId, SocialN void MastodonNotificationsSyncAdaptor::beginSync(int accountId, const QString &accessToken) { + m_accessTokens.insert(accountId, accessToken); m_pendingSyncStates.remove(accountId); requestUnreadMarker(accountId, accessToken); } @@ -206,7 +330,7 @@ void MastodonNotificationsSyncAdaptor::beginSync(int accountId, const QString &a void MastodonNotificationsSyncAdaptor::finalize(int accountId) { if (syncAborted()) { - qCInfo(lcSocialPlugin) << "sync aborted, won't update notifications"; + qCInfo(lcMastodonNotifications) << "sync aborted, won't update notifications"; } Q_UNUSED(accountId) @@ -214,52 +338,12 @@ void MastodonNotificationsSyncAdaptor::finalize(int accountId) QString MastodonNotificationsSyncAdaptor::sanitizeContent(const QString &content) { - QString plain = content; - plain.replace(QRegularExpression(QStringLiteral("<\\s*br\\s*/?\\s*>"), QRegularExpression::CaseInsensitiveOption), QStringLiteral("\n")); - plain.replace(QRegularExpression(QStringLiteral("<\\s*/\\s*p\\s*>"), QRegularExpression::CaseInsensitiveOption), QStringLiteral("\n")); - plain.remove(QRegularExpression(QStringLiteral("<[^>]+>"), QRegularExpression::CaseInsensitiveOption)); - - return decodeHtmlEntities(plain).trimmed(); + return MastodonTextUtils::sanitizeContent(content); } QDateTime MastodonNotificationsSyncAdaptor::parseTimestamp(const QString ×tampString) { - QDateTime timestamp; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) - timestamp = QDateTime::fromString(timestampString, Qt::ISODateWithMs); - if (timestamp.isValid()) { - return timestamp; - } -#endif - - timestamp = QDateTime::fromString(timestampString, Qt::ISODate); - if (timestamp.isValid()) { - return timestamp; - } - - const int timeSeparator = timestampString.indexOf(QLatin1Char('T')); - const int fractionSeparator = timestampString.indexOf(QLatin1Char('.'), timeSeparator + 1); - if (timeSeparator > -1 && fractionSeparator > -1) { - int timezoneSeparator = timestampString.indexOf(QLatin1Char('Z'), fractionSeparator + 1); - if (timezoneSeparator == -1) { - timezoneSeparator = timestampString.indexOf(QLatin1Char('+'), fractionSeparator + 1); - } - if (timezoneSeparator == -1) { - timezoneSeparator = timestampString.indexOf(QLatin1Char('-'), fractionSeparator + 1); - } - - QString stripped = timestampString; - if (timezoneSeparator > -1) { - stripped.remove(fractionSeparator, timezoneSeparator - fractionSeparator); - } else { - stripped.truncate(fractionSeparator); - } - - timestamp = QDateTime::fromString(stripped, Qt::ISODate); - } - - return timestamp; + return MastodonTextUtils::parseTimestamp(timestampString); } int MastodonNotificationsSyncAdaptor::compareNotificationIds(const QString &left, const QString &right) @@ -342,7 +426,7 @@ void MastodonNotificationsSyncAdaptor::requestUnreadMarker(int accountId, const incrementSemaphore(accountId); setupReplyTimeout(accountId, reply); } else { - qCWarning(lcSocialPlugin) << "unable to request notifications marker from Mastodon account with id" << accountId; + qCWarning(lcMastodonNotifications) << "unable to request notifications marker from Mastodon account with id" << accountId; } } @@ -365,8 +449,13 @@ void MastodonNotificationsSyncAdaptor::finishedUnreadMarkerHandler() bool ok = false; const QJsonObject markerObject = parseJsonObjectReplyData(replyData, &ok); if (isError || !ok) { - qCWarning(lcSocialPlugin) << "unable to parse notifications marker data from request with account" + qCWarning(lcMastodonNotifications) << "unable to parse notifications marker data from request with account" << accountId << ", got:" << QString::fromUtf8(replyData); + PendingSyncState fallbackState; + fallbackState.accessToken = accessToken; + fallbackState.lastFetchedId = loadLastFetchedId(accountId); + m_pendingSyncStates.insert(accountId, fallbackState); + requestNotifications(accountId, accessToken, fallbackState.lastFetchedId); decrementSemaphore(accountId); return; } @@ -380,6 +469,7 @@ void MastodonNotificationsSyncAdaptor::finishedUnreadMarkerHandler() PendingSyncState state; state.accessToken = accessToken; + state.markerKnown = true; state.unreadFloorId = markerId; state.lastFetchedId = loadLastFetchedId(accountId); if (state.lastFetchedId.isEmpty() && !markerId.isEmpty()) { @@ -424,7 +514,7 @@ void MastodonNotificationsSyncAdaptor::requestNotifications(int accountId, incrementSemaphore(accountId); setupReplyTimeout(accountId, reply); } else { - qCWarning(lcSocialPlugin) << "unable to request notifications from Mastodon account with id" << accountId; + qCWarning(lcMastodonNotifications) << "unable to request notifications from Mastodon account with id" << accountId; } } @@ -452,7 +542,7 @@ void MastodonNotificationsSyncAdaptor::requestMarkRead(int accountId, incrementSemaphore(accountId); setupReplyTimeout(accountId, reply); } else { - qCWarning(lcSocialPlugin) << "unable to update notifications marker for Mastodon account with id" << accountId; + qCWarning(lcMastodonNotifications) << "unable to update notifications marker for Mastodon account with id" << accountId; } } @@ -490,7 +580,10 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler() const QJsonArray notifications = parseJsonArrayReplyData(replyData, &ok); if (!isError && ok) { if (!notifications.size()) { - qCDebug(lcSocialPlugin) << "no notifications received for account" << accountId; + qCDebug(lcMastodonNotifications) << "no notifications received for account" << accountId; + if (state.markerKnown) { + closeAccountNotifications(accountId); + } m_pendingSyncStates.remove(accountId); decrementSemaphore(accountId); return; @@ -514,6 +607,10 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler() pageMinNotificationId = notificationId; } + if (state.markerKnown) { + state.unreadNotificationIds.insert(notificationId); + } + if (state.maxFetchedId.isEmpty() || compareNotificationIds(notificationId, state.maxFetchedId) > 0) { state.maxFetchedId = notificationId; @@ -530,6 +627,8 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler() const QJsonObject statusObject = statusValue.isObject() && !statusValue.isNull() ? statusValue.toObject() : QJsonObject(); + const QJsonObject eventObject = notificationObject.value(QStringLiteral("event")).toObject(); + const QJsonObject warningObject = notificationObject.value(QStringLiteral("moderation_warning")).toObject(); QDateTime eventTimestamp = parseTimestamp(notificationObject.value(QStringLiteral("created_at")).toString()); if (!eventTimestamp.isValid()) { @@ -545,9 +644,18 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler() const QString statusBody = sanitizeContent(statusObject.value(QStringLiteral("content")).toString()); const QString action = actionText(notificationType); QString body; - if (notificationType == QLatin1String("mention") + if (notificationType == QLatin1String("severed_relationships")) { + body = severedRelationshipsText(eventObject); + } else if (notificationType == QLatin1String("moderation_warning")) { + const QString warningText = moderationWarningText(warningObject); + body = warningText.isEmpty() + ? action + : QStringLiteral("%1: %2").arg(action, warningText); + } else if (notificationType == QLatin1String("mention") || notificationType == QLatin1String("status") - || notificationType == QLatin1String("update")) { + || notificationType == QLatin1String("update") + || notificationType == QLatin1String("quote") + || notificationType == QLatin1String("quoted_update")) { body = statusBody.isEmpty() ? action : statusBody; } else { body = statusBody.isEmpty() ? action : QStringLiteral("%1: %2").arg(action, statusBody); @@ -567,22 +675,30 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler() } else if (url.isEmpty() && !accountName.isEmpty()) { url = QStringLiteral("%1/@%2").arg(apiHost(accountId), accountName); } + if (useSystemSummary(notificationType)) { + url.clear(); + } PendingNotification pendingNotification; pendingNotification.notificationId = notificationId; - pendingNotification.summary = displayName; + pendingNotification.summary = useSystemSummary(notificationType) + ? qtTrId(TrIdMastodon) + : displayName; pendingNotification.body = body; pendingNotification.link = url; pendingNotification.timestamp = eventTimestamp; state.pendingNotifications.insert(notificationId, pendingNotification); } + const QString historyBoundaryId = !state.unreadFloorId.isEmpty() + ? state.unreadFloorId + : state.lastFetchedId; if (notifications.size() >= NotificationsPageLimit && !pageMinNotificationId.isEmpty() - && (state.unreadFloorId.isEmpty() - || compareNotificationIds(pageMinNotificationId, state.unreadFloorId) > 0)) { + && !historyBoundaryId.isEmpty() + && compareNotificationIds(pageMinNotificationId, historyBoundaryId) > 0) { m_pendingSyncStates.insert(accountId, state); - requestNotifications(accountId, state.accessToken, state.unreadFloorId, pageMinNotificationId); + requestNotifications(accountId, state.accessToken, historyBoundaryId, pageMinNotificationId); decrementSemaphore(accountId); return; } @@ -599,6 +715,10 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler() } } + if (state.markerKnown) { + closeAccountNotifications(accountId, state.unreadNotificationIds); + } + if (!state.maxFetchedId.isEmpty() && (state.lastFetchedId.isEmpty() || compareNotificationIds(state.maxFetchedId, state.lastFetchedId) > 0)) { @@ -611,13 +731,13 @@ void MastodonNotificationsSyncAdaptor::finishedNotificationsHandler() const QString currentMarkerId = m_lastMarkedReadIds.value(accountId); if (!markerId.isEmpty() && !state.accessToken.isEmpty() - && !hasActiveNotificationsForAccount(accountId) + && state.markerKnown && (currentMarkerId.isEmpty() || compareNotificationIds(markerId, currentMarkerId) > 0)) { - requestMarkRead(accountId, state.accessToken, markerId); + maybeMarkAccountNotificationsRead(accountId, state.accessToken); } } else { - qCWarning(lcSocialPlugin) << "unable to parse notifications data from request with account" << accountId + qCWarning(lcMastodonNotifications) << "unable to parse notifications data from request with account" << accountId << ", got:" << QString::fromUtf8(replyData); } @@ -649,7 +769,7 @@ void MastodonNotificationsSyncAdaptor::finishedMarkReadHandler() m_lastMarkedReadIds.insert(accountId, lastReadId); } } else { - qCWarning(lcSocialPlugin) << "unable to update notifications marker for account" << accountId + qCWarning(lcMastodonNotifications) << "unable to update notifications marker for account" << accountId << ", got:" << QString::fromUtf8(replyData); } @@ -665,10 +785,10 @@ void MastodonNotificationsSyncAdaptor::publishSystemNotification(int accountId, ? notificationData.timestamp : QDateTime::currentDateTimeUtc()); notification->setSummary(notificationData.summary.isEmpty() - ? QStringLiteral("Mastodon") + ? qtTrId(TrIdMastodon) : notificationData.summary); notification->setBody(notificationData.body.isEmpty() - ? QStringLiteral("New notification") + ? qtTrId(TrIdNewNotification) : notificationData.body); notification->setPreviewSummary(notificationData.summary); notification->setPreviewBody(notificationData.body); @@ -686,11 +806,75 @@ void MastodonNotificationsSyncAdaptor::publishSystemNotification(int accountId, notification->setRemoteAction(OPEN_URL_ACTION(authorizeInteractionUrl(apiHost(accountId), safeOpenUrl))); notification->publish(); if (notification->replacesId() == 0) { - qCWarning(lcSocialPlugin) << "failed to publish Mastodon notification" + qCWarning(lcMastodonNotifications) << "failed to publish Mastodon notification" << notificationData.notificationId; } } +void MastodonNotificationsSyncAdaptor::notificationClosedWithReason(uint reason) +{ + Notification *notification = qobject_cast<Notification *>(sender()); + removeCachedNotification(notification); + if (reason == NotificationDismissedReason) { + markReadFromNotification(notification); + } +} + +void MastodonNotificationsSyncAdaptor::maybeMarkAccountNotificationsRead(int accountId, + const QString &accessToken, + Notification *ignoredNotification) +{ + if (accountId <= 0 || accessToken.isEmpty()) { + return; + } + + if (hasActiveNotificationsForAccount(accountId, ignoredNotification)) { + return; + } + + const QString lastReadId = loadLastFetchedId(accountId); + if (lastReadId.isEmpty()) { + return; + } + + const QString currentMarkerId = m_lastMarkedReadIds.value(accountId); + if (!currentMarkerId.isEmpty() && compareNotificationIds(lastReadId, currentMarkerId) <= 0) { + return; + } + + requestMarkRead(accountId, accessToken, lastReadId); +} + +void MastodonNotificationsSyncAdaptor::markReadFromNotification(Notification *notification) +{ + if (!notification) { + return; + } + + const int accountId = notification->hintValue("x-nemo.sociald.account-id").toInt(); + const QString accessToken = m_accessTokens.value(accountId).trimmed(); + if (accountId <= 0 || accessToken.isEmpty()) { + return; + } + + maybeMarkAccountNotificationsRead(accountId, accessToken, notification); +} + +void MastodonNotificationsSyncAdaptor::removeCachedNotification(Notification *notification) +{ + if (!notification) { + return; + } + + const int accountId = notification->hintValue("x-nemo.sociald.account-id").toInt(); + const QString notificationId = notification->hintValue(NotificationIdHint).toString(); + if (accountId <= 0 || notificationId.isEmpty()) { + return; + } + + m_notificationObjects.remove(notificationObjectKey(accountId, notificationId)); +} + void MastodonNotificationsSyncAdaptor::closeAccountNotifications(int accountId, const QSet<QString> &keepNotificationIds) { @@ -755,6 +939,7 @@ Notification *MastodonNotificationsSyncAdaptor::createNotification(int accountId notification->setHintValue("x-nemo-priority", 100); // Show on lockscreen notification->setCategory(QLatin1String(NotificationCategory)); + connect(notification, SIGNAL(closed(uint)), this, SLOT(notificationClosedWithReason(uint)), Qt::UniqueConnection); m_notificationObjects.insert(objectKey, notification); return notification; |
