summaryrefslogtreecommitdiff
path: root/eventsview-plugins
diff options
context:
space:
mode:
authorAndrew Branson <andrew.branson@jolla.com>2026-04-03 22:55:30 +0200
committerAndrew Branson <andrew.branson@jolla.com>2026-04-04 11:55:25 +0200
commita35c9fa159173388d88ef77e1d31f53488aad094 (patch)
treee4691b5bbf054ca13e35d98d9df653bf9cdc0054 /eventsview-plugins
parent5f999f7a4712c4a4d1c89054b544064cfd4b769e (diff)
Generalize for all fediverse accounts
Diffstat (limited to 'eventsview-plugins')
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml362
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/eventsview-plugin-fediverse.pro (renamed from eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro)19
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/fediverse-delegate.qml185
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/fediversepostactions.cpp (renamed from eventsview-plugins/eventsview-plugin-mastodon/mastodonpostactions.cpp)38
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/fediversepostactions.h (renamed from eventsview-plugins/eventsview-plugin-mastodon/mastodonpostactions.h)10
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/fediversepostsmodel.cpp200
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/fediversepostsmodel.h (renamed from eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.h)19
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/plugin.cpp27
-rw-r--r--eventsview-plugins/eventsview-plugin-fediverse/qmldir4
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml488
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml214
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp165
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp27
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/qmldir4
-rw-r--r--eventsview-plugins/eventsview-plugins.pro2
15 files changed, 823 insertions, 941 deletions
diff --git a/eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml b/eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml
new file mode 100644
index 0000000..16dc191
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-fediverse/FediverseFeedItem.qml
@@ -0,0 +1,362 @@
+/*
+ * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import Sailfish.Share 1.0
+import Sailfish.TextLinking 1.0
+import org.nemomobile.lipstick 0.1
+import "shared"
+
+SocialMediaFeedItem {
+ id: item
+
+ property variant imageList
+ property string resolvedStatusUrl: model && model.url ? model.url.toString() : ""
+ property string postId
+ property QtObject postActions
+ property int likeCount: model && model.favouritesCount ? model.favouritesCount : 0
+ property int commentCount: model && model.repliesCount ? model.repliesCount : 0
+ property int boostCount: model && model.reblogsCount ? model.reblogsCount : 0
+ property bool favourited: model ? !!model.favourited : false
+ property bool reblogged: model ? !!model.reblogged : false
+ property int _likeCountOverride: -1
+ property int _boostCountOverride: -1
+ property int _favouritedOverride: -1
+ property int _rebloggedOverride: -1
+ property bool isFavourited: _favouritedOverride >= 0 ? _favouritedOverride === 1 : favourited
+ property bool isReblogged: _rebloggedOverride >= 0 ? _rebloggedOverride === 1 : reblogged
+ readonly property bool housekeeping: Lipstick.compositor.eventsLayer.housekeeping
+ readonly property bool lockScreenActive: Lipstick.compositor.lockScreenLayer.deviceIsLocked
+ property bool _pendingOpenActionMenu: false
+ property bool _contextMenuOpen: false
+ property var _actionMenu
+ property real _contextMenuHeight: (_contextMenuOpen && _actionMenu) ? _actionMenu.height : 0
+
+ property string _booster: model && model.boostedBy ? model.boostedBy.toString() : ""
+ property string _displayName: model && model.name ? model.name.toString() : ""
+ property string _accountName: model && model.accountName ? model.accountName.toString() : ""
+ property string _bodyText: model && model.body ? model.body.toString() : ""
+ //: Action label shown in Fediverse interaction menu.
+ //% "Favourite"
+ readonly property string _favouriteActionText: qsTrId("lipstick-jolla-home-la-fediverse_favourite")
+ //: Action label shown in Fediverse interaction menu when the post is already favourited.
+ //% "Unfavourite"
+ readonly property string _unfavouriteActionText: qsTrId("lipstick-jolla-home-la-fediverse_unfavourite")
+ //: Action label shown in Fediverse interaction menu.
+ //% "Boost"
+ readonly property string _boostActionText: qsTrId("lipstick-jolla-home-la-fediverse_boost")
+ //: Action label shown in Fediverse interaction menu when the post is already boosted.
+ //% "Undo boost"
+ readonly property string _unboostActionText: qsTrId("lipstick-jolla-home-la-fediverse_unboost")
+ //: Action label shown in Fediverse interaction menu.
+ //% "Share"
+ readonly property string _shareActionText: qsTrId("lipstick-jolla-home-la-fediverse_share")
+ //: Link title used when sharing a Fediverse post.
+ //% "Post from Fediverse"
+ readonly property string _shareLinkTitle: qsTrId("lipstick-jolla-home-la-fediverse_share_link_title")
+ property var _shareAction: ShareAction {
+ title: item._shareActionText
+ }
+
+ timestamp: model.timestamp
+ onRefreshTimeCountChanged: formattedTime = Format.formatDate(model.timestamp, Format.TimeElapsed)
+ onLockScreenActiveChanged: {
+ if (lockScreenActive && _actionMenu) {
+ _actionMenu.close()
+ }
+ }
+ onPressAndHold: function(mouse) {
+ if (mouse) {
+ mouse.accepted = true
+ }
+ _pendingOpenActionMenu = !lockScreenActive
+ && postActions
+ && actionPostId().length > 0
+ && actionAccountId() >= 0
+ openActionMenuTimer.restart()
+ }
+ onHousekeepingChanged: {
+ if (housekeeping && _pendingOpenActionMenu) {
+ Lipstick.compositor.eventsLayer.setHousekeeping(false)
+ }
+ }
+ Component.onDestruction: {
+ if (_actionMenu) {
+ _actionMenu.destroy()
+ _actionMenu = null
+ }
+ }
+
+ avatar.y: item._booster.length > 0
+ ? topMargin + boosterIcon.height + Theme.paddingSmall
+ : topMargin
+ contentHeight: Math.max(content.y + content.height, avatar.y + avatar.height) + bottomMargin + _contextMenuHeight
+ topMargin: item._booster.length > 0 ? Theme.paddingMedium : Theme.paddingLarge
+ userRemovable: false
+
+ SocialReshareIcon {
+ id: boosterIcon
+
+ anchors {
+ right: avatar.right
+ top: parent.top
+ topMargin: item.topMargin
+ }
+ visible: item._booster.length > 0
+ highlighted: item.highlighted
+ iconSource: "image://theme/icon-s-repost"
+ }
+
+ SocialReshareText {
+ anchors {
+ left: content.left
+ right: content.right
+ verticalCenter: boosterIcon.verticalCenter
+ }
+ highlighted: item.highlighted
+ text: item._booster.length > 0
+ ? //: Shown above a post that is boosted by another user. %1 = name of user who boosted
+ //% "%1 boosted"
+ qsTrId("lipstick-jolla-home-la-boosted_by").arg(item._booster)
+ : ""
+ }
+
+ Column {
+ id: content
+
+ anchors {
+ left: avatar.right
+ leftMargin: Theme.paddingMedium
+ top: avatar.top
+ }
+ width: parent.width - x
+
+ Label {
+ width: parent.width
+ truncationMode: TruncationMode.Fade
+ text: item._displayName
+ color: item.highlighted ? Theme.highlightColor : Theme.primaryColor
+ textFormat: Text.PlainText
+ }
+
+ Label {
+ width: parent.width
+ truncationMode: TruncationMode.Fade
+ text: item._accountName.length > 0 && item._accountName.charAt(0) !== "@"
+ ? "@" + item._accountName
+ : item._accountName
+ font.pixelSize: Theme.fontSizeSmall
+ color: item.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ textFormat: Text.PlainText
+ }
+
+ LinkedText {
+ width: parent.width
+ elide: Text.ElideRight
+ wrapMode: Text.Wrap
+ font.pixelSize: Theme.fontSizeSmall
+ shortenUrl: true
+ color: item.highlighted ? Theme.highlightColor : Theme.primaryColor
+ linkColor: Theme.highlightColor
+ plainText: item._bodyText
+ }
+
+ SocialPostMetadataRow {
+ id: metadataRow
+
+ width: parent.width
+ highlighted: item.highlighted
+ commentCount: item.commentCount
+ likeCount: item._likeCountOverride >= 0 ? item._likeCountOverride : item.likeCount
+ repostCount: item._boostCountOverride >= 0 ? item._boostCountOverride : item.boostCount
+ liked: item.isFavourited
+ reposted: item.isReblogged
+ timeText: item.formattedTime
+ addBottomPadding: previewRow.visible
+ }
+
+ SocialMediaPreviewRow {
+ id: previewRow
+
+ width: parent.width + Theme.horizontalPageMargin // extend to right edge of notification area
+ imageList: item.imageList
+ downloader: item.downloader
+ accountId: item.accountId
+ connectedToNetwork: item.connectedToNetwork
+ highlighted: item.highlighted
+ eventsColumnMaxWidth: item.eventsColumnMaxWidth - item.avatar.width
+ }
+ }
+
+ function actionPostId() {
+ if (item.postId.length > 0) {
+ return item.postId
+ }
+ return model && model.fediverseId ? model.fediverseId.toString() : ""
+ }
+
+ function actionAccountId() {
+ var parsed = Number(item.accountId)
+ return isNaN(parsed) ? -1 : parsed
+ }
+
+ function shareStatusUrl() {
+ return model && model.url ? model.url.toString() : ""
+ }
+
+ function topLevelParent() {
+ var p = item
+ while (p && p.parent) {
+ p = p.parent
+ }
+ return p
+ }
+
+ function openActionMenu() {
+ if (_actionMenu) {
+ _actionMenu.destroy()
+ _actionMenu = null
+ }
+
+ var parentItem = topLevelParent()
+ _actionMenu = actionMenuComponent.createObject(parentItem)
+ if (_actionMenu) {
+ _actionMenu.open(item)
+ }
+ }
+
+ Connections {
+ target: item.postActions ? item.postActions : null
+
+ onActionSucceeded: {
+ if (accountId !== item.actionAccountId() || statusId !== item.actionPostId()) {
+ return
+ }
+
+ if (favouritesCount >= 0) {
+ item._likeCountOverride = favouritesCount
+ }
+ if (reblogsCount >= 0) {
+ item._boostCountOverride = reblogsCount
+ }
+ item._favouritedOverride = favourited ? 1 : 0
+ item._rebloggedOverride = reblogged ? 1 : 0
+ item._contextMenuOpen = false
+
+ if (item._accountDelegate) {
+ item._accountDelegate.sync()
+ }
+ }
+
+ onActionFailed: {
+ if (accountId !== item.actionAccountId() || statusId !== item.actionPostId()) {
+ return
+ }
+ console.warn("Fediverse action failed:", action, errorMessage)
+ item._contextMenuOpen = false
+ }
+ }
+
+ Component {
+ id: actionMenuComponent
+
+ SocialInteractionContextMenu {
+ id: actionMenu
+ z: 10000
+ mapSourceItem: _contentColumn
+ actionEnabled: item.postActions
+ && item.actionPostId().length > 0
+ && item.actionAccountId() >= 0
+ && !item.lockScreenActive
+ && !item.housekeeping
+ interactionItems: [
+ {
+ name: "like",
+ // U+2605 BLACK STAR
+ symbol: "\u2605",
+ active: item.isFavourited,
+ inactiveText: item._favouriteActionText,
+ activeText: item._unfavouriteActionText
+ },
+ {
+ name: "reblog",
+ // U+21BB CLOCKWISE OPEN CIRCLE ARROW
+ symbol: "\u21BB",
+ active: item.isReblogged,
+ inactiveText: item._boostActionText,
+ activeText: item._unboostActionText
+ },
+ {
+ name: "share",
+ // U+260D OPPOSITION (ironic doncha think)
+ symbol: "\u260D",
+ active: false,
+ inactiveText: item._shareActionText,
+ activeText: item._shareActionText
+ }
+ ]
+
+ onInteractionMenuOpened: item._contextMenuOpen = true
+ onInteractionMenuClosed: {
+ item._contextMenuOpen = false
+ destroy()
+ item._actionMenu = null
+ }
+
+ onInteractionTriggered: function(actionName) {
+ if (!actionEnabled) {
+ return
+ }
+ var postId = item.actionPostId()
+ var accountId = item.actionAccountId()
+ if (actionName === "like") {
+ if (item.isFavourited) {
+ item.postActions.unfavourite(accountId, postId)
+ } else {
+ item.postActions.favourite(accountId, postId)
+ }
+ } else if (actionName === "reblog") {
+ if (item.isReblogged) {
+ item.postActions.unboost(accountId, postId)
+ } else {
+ item.postActions.boost(accountId, postId)
+ }
+ } else if (actionName === "share") {
+ var shareUrl = item.shareStatusUrl()
+ if (shareUrl.length === 0) {
+ return
+ }
+ item._shareAction.resources = [{
+ "data": shareUrl,
+ "linkTitle": item._shareLinkTitle,
+ "type": "text/x-url"
+ }]
+ item._shareAction.trigger()
+ }
+ }
+ }
+ }
+
+ Timer {
+ id: openActionMenuTimer
+
+ interval: 0
+ repeat: false
+ onTriggered: {
+ if (item.lockScreenActive) {
+ item._pendingOpenActionMenu = false
+ return
+ }
+ Lipstick.compositor.eventsLayer.setHousekeeping(false)
+ if (item._pendingOpenActionMenu) {
+ item._contextMenuOpen = false
+ item.openActionMenu()
+ }
+ item._pendingOpenActionMenu = false
+ }
+ }
+}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro b/eventsview-plugins/eventsview-plugin-fediverse/eventsview-plugin-fediverse.pro
index 04be215..e546e9d 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro
+++ b/eventsview-plugins/eventsview-plugin-fediverse/eventsview-plugin-fediverse.pro
@@ -3,20 +3,21 @@
# SPDX-License-Identifier: BSD-3-Clause
TEMPLATE = lib
-TARGET = jollaeventsviewmastodonplugin
+TARGET = jollaeventsviewfediverseplugin
TARGET = $$qtLibraryTarget($$TARGET)
-MODULENAME = com/jolla/eventsview/mastodon
+MODULENAME = com/jolla/eventsview/fediverse
TARGETPATH = $$[QT_INSTALL_QML]/$$MODULENAME
+QT -= gui
QT += qml network
CONFIG += plugin link_pkgconfig
PKGCONFIG += socialcache accounts-qt5 libsignon-qt5 sailfishaccounts
include($$PWD/../../common/common.pri)
-TS_FILE = $$OUT_PWD/lipstick-jolla-home-mastodon.ts
-EE_QM = $$OUT_PWD/lipstick-jolla-home-mastodon_eng_en.qm
+TS_FILE = $$OUT_PWD/lipstick-jolla-home-fediverse.ts
+EE_QM = $$OUT_PWD/lipstick-jolla-home-fediverse_eng_en.qm
ts.commands += lupdate $$PWD -ts $$TS_FILE
ts.CONFIG += no_check_exist no_link
@@ -44,15 +45,15 @@ PRE_TARGETDEPS += ts engineering_english
INSTALLS += ts_install engineering_english_install
HEADERS += \
- mastodonpostactions.h \
- mastodonpostsmodel.h
+ fediversepostactions.h \
+ fediversepostsmodel.h
SOURCES += \
- mastodonpostactions.cpp \
- mastodonpostsmodel.cpp \
+ fediversepostactions.cpp \
+ fediversepostsmodel.cpp \
plugin.cpp
-qml.files = mastodon-delegate.qml MastodonFeedItem.qml
+qml.files = fediverse-delegate.qml FediverseFeedItem.qml
qml.path = /usr/share/lipstick/eventfeed/
import.files = qmldir
diff --git a/eventsview-plugins/eventsview-plugin-fediverse/fediverse-delegate.qml b/eventsview-plugins/eventsview-plugin-fediverse/fediverse-delegate.qml
new file mode 100644
index 0000000..f954db4
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-fediverse/fediverse-delegate.qml
@@ -0,0 +1,185 @@
+/*
+ * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import org.nemomobile.socialcache 1.0
+import com.jolla.eventsview.fediverse 1.0
+import QtQml.Models 2.1
+import "shared"
+
+SocialMediaAccountDelegate {
+ id: delegateItem
+ property string instanceHomeUrl: ""
+ property string instanceIconPath: ""
+
+ //: Fediverse posts
+ //% "Posts"
+ headerText: qsTrId("lipstick-jolla-home-la-fediverse_posts")
+ headerIcon: delegateItem.instanceIconPath.length > 0 ? delegateItem.instanceIconPath : "image://theme/icon-l-fediverse"
+ showRemainingCount: false
+
+ services: ["Posts"]
+ socialNetwork: SocialSync.Fediverse
+ dataType: SocialSync.Posts
+ providerName: "fediverse"
+ periodicSyncLoopEnabled: true
+
+ FediversePostActions {
+ id: fediversePostActions
+ }
+
+ model: FediversePostsModel {}
+
+ delegate: FediverseFeedItem {
+ downloader: delegateItem.downloader
+ imageList: model.images
+ avatarSource: model.icon
+ fallbackAvatarSource: model.icon
+ resolvedStatusUrl: delegateItem.authorizeInteractionUrl(model)
+ postId: model.fediverseId
+ postActions: fediversePostActions
+ accountId: delegateItem.firstAccountId(model, -1)
+
+ onTriggered: {
+ if (resolvedStatusUrl.length > 0) {
+ Qt.openUrlExternally(resolvedStatusUrl)
+ }
+ }
+
+ Component.onCompleted: {
+ delegateItem.instanceHomeUrl = statusUrl({instanceUrl: model.instanceUrl})
+ if (model.instanceIconPath && model.instanceIconPath.length > 0) {
+ delegateItem.instanceIconPath = model.instanceIconPath
+ }
+ refreshTimeCount = Qt.binding(function() { return delegateItem.refreshTimeCount })
+ connectedToNetwork = Qt.binding(function() { return delegateItem.connectedToNetwork })
+ eventsColumnMaxWidth = Qt.binding(function() { return delegateItem.eventsColumnMaxWidth })
+ }
+ }
+ //% "Show more in Fediverse"
+ expandedLabel: qsTrId("lipstick-jolla-home-la-show-more-in-fediverse")
+
+ onHeaderClicked: {
+ if (delegateItem.instanceHomeUrl.length > 0) {
+ Qt.openUrlExternally(delegateItem.instanceHomeUrl)
+ }
+ }
+ onExpandedClicked: {
+ if (delegateItem.instanceHomeUrl.length > 0) {
+ Qt.openUrlExternally(delegateItem.instanceHomeUrl)
+ }
+ }
+
+ onViewVisibleChanged: {
+ if (viewVisible) {
+ delegateItem.resetHasSyncableAccounts()
+ delegateItem.model.refresh()
+ if (delegateItem.hasSyncableAccounts) {
+ delegateItem.startPeriodicSyncLoop()
+ }
+ } else {
+ delegateItem.stopPeriodicSyncLoop()
+ }
+ }
+
+ onConnectedToNetworkChanged: {
+ if (viewVisible) {
+ delegateItem.startPeriodicSyncLoop()
+ }
+ }
+
+ Connections {
+ target: delegateItem.model
+
+ onCountChanged: {
+ if (target.count === 0) {
+ delegateItem.instanceHomeUrl = ""
+ delegateItem.instanceIconPath = ""
+ }
+ }
+ }
+
+ function statusUrl(modelData) {
+ var directUrl = modelData && modelData.url ? modelData.url.toString() : ""
+ if (directUrl.length > 0) {
+ return directUrl
+ }
+
+ var instanceUrl = modelData && modelData.instanceUrl ? modelData.instanceUrl.toString() : ""
+ instanceUrl = stripTrailingSlashes(instanceUrl)
+ if (instanceUrl.length === 0) {
+ return ""
+ }
+
+ var accountName = modelData && modelData.accountName ? modelData.accountName.toString() : ""
+ var statusId = modelData && modelData.fediverseId ? modelData.fediverseId.toString() : ""
+ if (accountName.length > 0 && statusId.length > 0) {
+ accountName = trimLeadingCharacter(accountName, "@")
+ return instanceUrl + "/@" + accountName + "/" + statusId
+ }
+
+ return instanceUrl + "/explore"
+ }
+
+ function authorizeInteractionUrl(modelData) {
+ var targetUrl = statusUrl(modelData)
+ if (targetUrl.length === 0) {
+ return targetUrl
+ }
+
+ var instanceUrl = modelData && modelData.instanceUrl ? modelData.instanceUrl.toString() : ""
+ if (instanceUrl.length === 0) {
+ return targetUrl
+ }
+ instanceUrl = stripTrailingSlashes(instanceUrl)
+
+ // Links on the user's own instance should open directly.
+ var sameServer = /^([a-z][a-z0-9+.-]*):\/\/([^\/?#]+)/i
+ var targetMatch = targetUrl.match(sameServer)
+ var instanceMatch = instanceUrl.match(sameServer)
+ if (targetMatch && instanceMatch
+ && targetMatch.length > 2
+ && instanceMatch.length > 2
+ && targetMatch[1].toLowerCase() === instanceMatch[1].toLowerCase()
+ && targetMatch[2].toLowerCase() === instanceMatch[2].toLowerCase()) {
+ return targetUrl
+ }
+
+ return instanceUrl + "/authorize_interaction?uri=" + encodeURIComponent(targetUrl)
+ }
+
+ function firstAccountId(modelData, defaultValue) {
+ var fallback = typeof defaultValue === "undefined" ? -1 : Number(defaultValue)
+ var accounts = modelData ? modelData.accounts : undefined
+ if (!accounts || accounts.length <= 0) {
+ return fallback
+ }
+
+ var accountId = Number(accounts[0])
+ return isNaN(accountId) ? fallback : accountId
+ }
+
+ function stripTrailingSlashes(value) {
+ value = String(value || "")
+ while (value.length > 0 && value.charAt(value.length - 1) === "/") {
+ value = value.slice(0, value.length - 1)
+ }
+ return value
+ }
+
+ function trimLeadingCharacter(value, character) {
+ value = String(value || "")
+ if (!character || character.length === 0) {
+ return value
+ }
+
+ while (value.length > 0 && value.charAt(0) === character) {
+ value = value.substring(1)
+ }
+ return value
+ }
+}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostactions.cpp b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostactions.cpp
index 371b7dd..2b370f2 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostactions.cpp
+++ b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostactions.cpp
@@ -16,9 +16,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "mastodonpostactions.h"
+#include "fediversepostactions.h"
-#include "mastodonauthutils.h"
+#include "fediverseauthutils.h"
#include <Accounts/Account>
#include <Accounts/AccountService>
@@ -39,36 +39,36 @@
#include <QtDebug>
namespace {
- const char *const MicroblogServiceName = "mastodon-microblog";
+ const char *const MicroblogServiceName = "fediverse-microblog";
}
-MastodonPostActions::MastodonPostActions(QObject *parent)
+FediversePostActions::FediversePostActions(QObject *parent)
: QObject(parent)
, m_accountManager(new Accounts::Manager(this))
{
}
-void MastodonPostActions::favourite(int accountId, const QString &statusId)
+void FediversePostActions::favourite(int accountId, const QString &statusId)
{
performAction(accountId, statusId, QStringLiteral("favourite"));
}
-void MastodonPostActions::unfavourite(int accountId, const QString &statusId)
+void FediversePostActions::unfavourite(int accountId, const QString &statusId)
{
performAction(accountId, statusId, QStringLiteral("unfavourite"));
}
-void MastodonPostActions::boost(int accountId, const QString &statusId)
+void FediversePostActions::boost(int accountId, const QString &statusId)
{
performAction(accountId, statusId, QStringLiteral("reblog"));
}
-void MastodonPostActions::unboost(int accountId, const QString &statusId)
+void FediversePostActions::unboost(int accountId, const QString &statusId)
{
performAction(accountId, statusId, QStringLiteral("unreblog"));
}
-void MastodonPostActions::performAction(int accountId, const QString &statusId, const QString &action)
+void FediversePostActions::performAction(int accountId, const QString &statusId, const QString &action)
{
const QString trimmedStatusId = statusId.trimmed();
if (accountId <= 0 || trimmedStatusId.isEmpty() || action.isEmpty()) {
@@ -116,7 +116,7 @@ void MastodonPostActions::performAction(int accountId, const QString &statusId,
}
QVariantMap signonSessionData = accountService.authData().parameters();
- MastodonAuthUtils::addSignOnSessionParameters(account, &signonSessionData);
+ FediverseAuthUtils::addSignOnSessionParameters(account, &signonSessionData);
connect(session, SIGNAL(response(SignOn::SessionData)),
this, SLOT(signOnResponse(SignOn::SessionData)),
@@ -135,7 +135,7 @@ void MastodonPostActions::performAction(int accountId, const QString &statusId,
session->process(SignOn::SessionData(signonSessionData), mechanism);
}
-void MastodonPostActions::signOnResponse(const SignOn::SessionData &responseData)
+void FediversePostActions::signOnResponse(const SignOn::SessionData &responseData)
{
QObject *sessionObject = sender();
SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession *>(sessionObject);
@@ -148,12 +148,12 @@ void MastodonPostActions::signOnResponse(const SignOn::SessionData &responseData
const QString action = session->property("action").toString();
const QString key = actionKey(accountId, statusId, action);
- const QVariantMap data = MastodonAuthUtils::responseDataToMap(responseData);
- const QString accessToken = MastodonAuthUtils::accessToken(data);
+ const QVariantMap data = FediverseAuthUtils::responseDataToMap(responseData);
+ const QString accessToken = FediverseAuthUtils::accessToken(data);
Accounts::Account *account = session->property("account").value<Accounts::Account *>();
const QString apiHost = account
- ? MastodonAuthUtils::normalizeApiHost(account->value(QStringLiteral("api/Host")).toString())
+ ? FediverseAuthUtils::normalizeApiHost(account->value(QStringLiteral("api/Host")).toString())
: QString();
if (accessToken.isEmpty() || apiHost.isEmpty()) {
@@ -167,7 +167,7 @@ void MastodonPostActions::signOnResponse(const SignOn::SessionData &responseData
executeActionRequest(accountId, statusId, action, apiHost, accessToken);
}
-void MastodonPostActions::signOnError(const SignOn::Error &error)
+void FediversePostActions::signOnError(const SignOn::Error &error)
{
QObject *sessionObject = sender();
SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession *>(sessionObject);
@@ -185,7 +185,7 @@ void MastodonPostActions::signOnError(const SignOn::Error &error)
releaseSignOnObjects(sessionObject);
}
-void MastodonPostActions::executeActionRequest(int accountId,
+void FediversePostActions::executeActionRequest(int accountId,
const QString &statusId,
const QString &action,
const QString &apiHost,
@@ -212,7 +212,7 @@ void MastodonPostActions::executeActionRequest(int accountId,
connect(reply, SIGNAL(finished()), this, SLOT(actionFinishedHandler()));
}
-void MastodonPostActions::actionFinishedHandler()
+void FediversePostActions::actionFinishedHandler()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if (!reply) {
@@ -291,7 +291,7 @@ void MastodonPostActions::actionFinishedHandler()
favourited, reblogged);
}
-void MastodonPostActions::releaseSignOnObjects(QObject *sessionObject)
+void FediversePostActions::releaseSignOnObjects(QObject *sessionObject)
{
SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession *>(sessionObject);
if (!session) {
@@ -311,7 +311,7 @@ void MastodonPostActions::releaseSignOnObjects(QObject *sessionObject)
}
}
-QString MastodonPostActions::actionKey(int accountId, const QString &statusId, const QString &action) const
+QString FediversePostActions::actionKey(int accountId, const QString &statusId, const QString &action) const
{
return QString::number(accountId) + QLatin1Char(':') + statusId + QLatin1Char(':') + action;
}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostactions.h b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostactions.h
index cfe0c2a..536d339 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostactions.h
+++ b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostactions.h
@@ -16,8 +16,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef MASTODONPOSTACTIONS_H
-#define MASTODONPOSTACTIONS_H
+#ifndef FEDIVERSEPOSTACTIONS_H
+#define FEDIVERSEPOSTACTIONS_H
#include <QtCore/QObject>
#include <QtCore/QSet>
@@ -34,12 +34,12 @@ class Error;
class SessionData;
}
-class MastodonPostActions : public QObject
+class FediversePostActions : public QObject
{
Q_OBJECT
public:
- explicit MastodonPostActions(QObject *parent = 0);
+ explicit FediversePostActions(QObject *parent = 0);
Q_INVOKABLE void favourite(int accountId, const QString &statusId);
Q_INVOKABLE void unfavourite(int accountId, const QString &statusId);
@@ -79,4 +79,4 @@ private:
QSet<QString> m_pendingActions;
};
-#endif // MASTODONPOSTACTIONS_H
+#endif // FEDIVERSEPOSTACTIONS_H
diff --git a/eventsview-plugins/eventsview-plugin-fediverse/fediversepostsmodel.cpp b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostsmodel.cpp
new file mode 100644
index 0000000..48b3446
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostsmodel.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2013-2026 Jolla Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "fediversepostsmodel.h"
+
+#include <QtCore/QVariantList>
+
+namespace {
+
+QVariantList imageListForPost(const SocialPost::ConstPtr &post)
+{
+ QVariantList images;
+ if (!post) {
+ return images;
+ }
+
+ const QList<SocialPostImage::ConstPtr> postImages = post->images();
+ for (const SocialPostImage::ConstPtr &image : postImages) {
+ if (!image) {
+ continue;
+ }
+
+ QVariantMap imageMap;
+ imageMap.insert(QStringLiteral("url"), image->url());
+ imageMap.insert(QStringLiteral("type"), image->type() == SocialPostImage::Video
+ ? QStringLiteral("video")
+ : QStringLiteral("image"));
+ images.append(imageMap);
+ }
+
+ return images;
+}
+
+QVariantList accountListForPost(const SocialPost::ConstPtr &post)
+{
+ QVariantList accounts;
+ if (!post) {
+ return accounts;
+ }
+
+ const QList<int> postAccounts = post->accounts();
+ for (int accountId : postAccounts) {
+ accounts.append(accountId);
+ }
+ return accounts;
+}
+
+void appendCommonPostFields(QMap<int, QVariant> *eventMap,
+ const SocialPost::ConstPtr &post,
+ int idRole,
+ int nameRole,
+ int bodyRole,
+ int timestampRole,
+ int iconRole,
+ int imagesRole,
+ int accountsRole)
+{
+ if (!eventMap || !post) {
+ return;
+ }
+
+ eventMap->insert(idRole, post->identifier());
+ eventMap->insert(nameRole, post->name());
+ eventMap->insert(bodyRole, post->body());
+ eventMap->insert(timestampRole, post->timestamp());
+ eventMap->insert(iconRole, post->icon());
+ eventMap->insert(imagesRole, imageListForPost(post));
+ eventMap->insert(accountsRole, accountListForPost(post));
+}
+
+}
+
+FediversePostsModel::FediversePostsModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+ connect(&m_database, &AbstractSocialPostCacheDatabase::postsChanged,
+ this, &FediversePostsModel::postsChanged);
+ connect(&m_database, SIGNAL(accountIdFilterChanged()),
+ this, SIGNAL(accountIdFilterChanged()));
+}
+
+int FediversePostsModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_data.count();
+}
+
+QVariant FediversePostsModel::data(const QModelIndex &index, int role) const
+{
+ const int row = index.row();
+ if (!index.isValid() || row < 0 || row >= m_data.count()) {
+ return QVariant();
+ }
+
+ return m_data.at(row).value(role);
+}
+
+QHash<int, QByteArray> FediversePostsModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames;
+ roleNames.insert(FediverseId, "fediverseId");
+ roleNames.insert(Name, "name");
+ roleNames.insert(AccountName, "accountName");
+ roleNames.insert(Acct, "acct");
+ roleNames.insert(Body, "body");
+ roleNames.insert(Timestamp, "timestamp");
+ roleNames.insert(Icon, "icon");
+ roleNames.insert(Images, "images");
+ roleNames.insert(Url, "url");
+ roleNames.insert(Link, "link");
+ roleNames.insert(BoostedBy, "boostedBy");
+ roleNames.insert(RebloggedBy, "rebloggedBy");
+ roleNames.insert(RepliesCount, "repliesCount");
+ roleNames.insert(FavouritesCount, "favouritesCount");
+ roleNames.insert(ReblogsCount, "reblogsCount");
+ roleNames.insert(Favourited, "favourited");
+ roleNames.insert(Reblogged, "reblogged");
+ roleNames.insert(InstanceUrl, "instanceUrl");
+ roleNames.insert(InstanceIconPath, "instanceIconPath");
+ roleNames.insert(Accounts, "accounts");
+ return roleNames;
+}
+
+QVariantList FediversePostsModel::accountIdFilter() const
+{
+ return m_database.accountIdFilter();
+}
+
+void FediversePostsModel::setAccountIdFilter(const QVariantList &accountIds)
+{
+ m_database.setAccountIdFilter(accountIds);
+}
+
+void FediversePostsModel::refresh()
+{
+ m_database.refresh();
+}
+
+void FediversePostsModel::postsChanged()
+{
+ QList<RowData> data;
+ QList<SocialPost::ConstPtr> postsData = m_database.posts();
+ Q_FOREACH (const SocialPost::ConstPtr &post, postsData) {
+ RowData eventMap;
+ const QString accountName = m_database.accountName(post);
+ const QString postUrl = m_database.url(post);
+ const QString boostedBy = m_database.boostedBy(post);
+ const int repliesCount = m_database.repliesCount(post);
+ const int favouritesCount = m_database.favouritesCount(post);
+ const int reblogsCount = m_database.reblogsCount(post);
+ const bool favourited = m_database.favourited(post);
+ const bool reblogged = m_database.reblogged(post);
+
+ appendCommonPostFields(&eventMap, post,
+ FediversePostsModel::FediverseId,
+ FediversePostsModel::Name,
+ FediversePostsModel::Body,
+ FediversePostsModel::Timestamp,
+ FediversePostsModel::Icon,
+ FediversePostsModel::Images,
+ FediversePostsModel::Accounts);
+ eventMap.insert(FediversePostsModel::AccountName, accountName);
+ eventMap.insert(FediversePostsModel::Acct, accountName);
+ eventMap.insert(FediversePostsModel::Url, postUrl);
+ eventMap.insert(FediversePostsModel::Link, postUrl);
+ eventMap.insert(FediversePostsModel::BoostedBy, boostedBy);
+ eventMap.insert(FediversePostsModel::RebloggedBy, boostedBy);
+ eventMap.insert(FediversePostsModel::RepliesCount, repliesCount);
+ eventMap.insert(FediversePostsModel::FavouritesCount, favouritesCount);
+ eventMap.insert(FediversePostsModel::ReblogsCount, reblogsCount);
+ eventMap.insert(FediversePostsModel::Favourited, favourited);
+ eventMap.insert(FediversePostsModel::Reblogged, reblogged);
+ eventMap.insert(FediversePostsModel::InstanceUrl, m_database.instanceUrl(post));
+ eventMap.insert(FediversePostsModel::InstanceIconPath, m_database.instanceIconPath(post));
+ data.append(eventMap);
+ }
+
+ const int oldCount = m_data.count();
+ beginResetModel();
+ m_data = data;
+ endResetModel();
+ if (oldCount != m_data.count()) {
+ emit countChanged();
+ }
+}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.h b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostsmodel.h
index e30437d..96acae3 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.h
+++ b/eventsview-plugins/eventsview-plugin-fediverse/fediversepostsmodel.h
@@ -16,22 +16,22 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef MASTODONPOSTSMODEL_H
-#define MASTODONPOSTSMODEL_H
+#ifndef FEDIVERSEPOSTSMODEL_H
+#define FEDIVERSEPOSTSMODEL_H
-#include "mastodonpostsdatabase.h"
+#include "fediversepostsdatabase.h"
#include <QtCore/QAbstractListModel>
#include <QtCore/QMap>
-class MastodonPostsModel: public QAbstractListModel
+class FediversePostsModel: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QVariantList accountIdFilter READ accountIdFilter WRITE setAccountIdFilter NOTIFY accountIdFilterChanged)
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public:
- enum MastodonPostsRole {
- MastodonId = 0,
+ enum FediversePostsRole {
+ FediverseId = 0,
Name,
AccountName,
Acct,
@@ -49,10 +49,11 @@ public:
Favourited,
Reblogged,
InstanceUrl,
+ InstanceIconPath,
Accounts
};
- explicit MastodonPostsModel(QObject *parent = 0);
+ explicit FediversePostsModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
@@ -73,7 +74,7 @@ private slots:
private:
typedef QMap<int, QVariant> RowData;
QList<RowData> m_data;
- MastodonPostsDatabase m_database;
+ FediversePostsDatabase m_database;
};
-#endif // MASTODONPOSTSMODEL_H
+#endif // FEDIVERSEPOSTSMODEL_H
diff --git a/eventsview-plugins/eventsview-plugin-fediverse/plugin.cpp b/eventsview-plugins/eventsview-plugin-fediverse/plugin.cpp
new file mode 100644
index 0000000..509b602
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-fediverse/plugin.cpp
@@ -0,0 +1,27 @@
+/*
+ * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <QQmlExtensionPlugin>
+#include <QtQml>
+
+#include "fediversepostactions.h"
+#include "fediversepostsmodel.h"
+
+class JollaEventsviewFediversePlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "com.jolla.eventsview.fediverse")
+
+public:
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("com.jolla.eventsview.fediverse"));
+ qmlRegisterType<FediversePostsModel>(uri, 1, 0, "FediversePostsModel");
+ qmlRegisterType<FediversePostActions>(uri, 1, 0, "FediversePostActions");
+ }
+};
+
+#include "plugin.moc"
diff --git a/eventsview-plugins/eventsview-plugin-fediverse/qmldir b/eventsview-plugins/eventsview-plugin-fediverse/qmldir
new file mode 100644
index 0000000..8b25e83
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-fediverse/qmldir
@@ -0,0 +1,4 @@
+# Copyright (C) 2013-2026 Jolla Ltd.
+
+module com.jolla.eventsview.fediverse
+plugin jollaeventsviewfediverseplugin
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml b/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml
deleted file mode 100644
index 63b9556..0000000
--- a/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-import QtQuick 2.0
-import Sailfish.Silica 1.0
-import Sailfish.Share 1.0
-import Sailfish.TextLinking 1.0
-import org.nemomobile.lipstick 0.1
-import "shared"
-
-SocialMediaFeedItem {
- id: item
-
- property variant imageList
- property string resolvedStatusUrl: item.stringValue("url", "link", "uri")
- property string postId
- property QtObject postActions
- property int likeCount: item.intValue("favouritesCount", "likeCount", "favoriteCount")
- property int commentCount: item.intValue("repliesCount", "commentCount")
- property int boostCount: item.intValue("reblogsCount", "boostCount", "repostsCount")
- property bool favourited: !!model.favourited
- property bool reblogged: !!model.reblogged
- property int _likeCountOverride: -1
- property int _boostCountOverride: -1
- property int _favouritedOverride: -1
- property int _rebloggedOverride: -1
- property bool isFavourited: _favouritedOverride >= 0 ? _favouritedOverride === 1 : favourited
- property bool isReblogged: _rebloggedOverride >= 0 ? _rebloggedOverride === 1 : reblogged
- readonly property bool housekeeping: Lipstick.compositor.eventsLayer.housekeeping
- readonly property bool lockScreenActive: Lipstick.compositor.lockScreenLayer.deviceIsLocked
- property bool _pendingOpenActionMenu: false
- property bool _contextMenuOpen: false
- property var _actionMenu
- property real _contextMenuHeight: (_contextMenuOpen && _actionMenu) ? _actionMenu.height : 0
-
- property string _booster: model && model.boostedBy ? model.boostedBy.toString() : ""
- property string _displayName: model && model.name ? model.name.toString() : ""
- property string _accountName: model && model.accountName ? model.accountName.toString() : ""
- property string _bodyText: model && model.body ? model.body.toString() : ""
- //: Action label shown in Mastodon interaction menu.
- //% "Share"
- readonly property string _shareActionText: qsTrId("lipstick-jolla-home-la-mastodon_share")
- //: Link title used when sharing a Mastodon post.
- //% "Post from Mastodon"
- readonly property string _shareLinkTitle: qsTrId("lipstick-jolla-home-la-mastodon_share_link_title")
- property var _shareAction: ShareAction {
- title: item._shareActionText
- }
-
- timestamp: model.timestamp
- onRefreshTimeCountChanged: formattedTime = Format.formatDate(model.timestamp, Format.TimeElapsed)
- onLockScreenActiveChanged: {
- if (lockScreenActive && _actionMenu) {
- _actionMenu.close()
- }
- }
- onPressAndHold: function(mouse) {
- if (mouse) {
- mouse.accepted = true
- }
- _pendingOpenActionMenu = !lockScreenActive
- && postActions
- && actionPostId().length > 0
- && actionAccountId() >= 0
- openActionMenuTimer.restart()
- }
- onHousekeepingChanged: {
- if (housekeeping && _pendingOpenActionMenu) {
- Lipstick.compositor.eventsLayer.setHousekeeping(false)
- }
- }
- Component.onDestruction: {
- if (_actionMenu) {
- _actionMenu.destroy()
- _actionMenu = null
- }
- }
-
- avatar.y: item._booster.length > 0
- ? topMargin + boosterIcon.height + Theme.paddingSmall
- : topMargin
- contentHeight: Math.max(content.y + content.height, avatar.y + avatar.height) + bottomMargin + _contextMenuHeight
- topMargin: item._booster.length > 0 ? Theme.paddingMedium : Theme.paddingLarge
- userRemovable: false
-
- Image {
- id: boosterIcon
-
- anchors {
- right: avatar.right
- top: parent.top
- topMargin: item.topMargin
- }
- visible: item._booster.length > 0
- source: "image://theme/icon-s-repost" + (item.highlighted ? "?" + Theme.highlightColor : "")
- }
-
- Text {
- anchors {
- left: content.left
- right: content.right
- verticalCenter: boosterIcon.verticalCenter
- }
- elide: Text.ElideRight
- font.pixelSize: Theme.fontSizeExtraSmall
- color: item.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
- textFormat: Text.PlainText
- visible: text.length > 0
-
- text: item._booster.length > 0
- ? //: Shown above a post that is boosted by another user. %1 = name of user who boosted
- //% "%1 boosted"
- qsTrId("lipstick-jolla-home-la-boosted_by").arg(item._booster)
- : ""
- }
-
- Column {
- id: content
-
- anchors {
- left: avatar.right
- leftMargin: Theme.paddingMedium
- top: avatar.top
- }
- width: parent.width - x
-
- Label {
- width: parent.width
- truncationMode: TruncationMode.Fade
- text: item._displayName
- color: item.highlighted ? Theme.highlightColor : Theme.primaryColor
- textFormat: Text.PlainText
- }
-
- Label {
- width: parent.width
- truncationMode: TruncationMode.Fade
- text: item._accountName.length > 0 && item._accountName.charAt(0) !== "@"
- ? "@" + item._accountName
- : item._accountName
- font.pixelSize: Theme.fontSizeSmall
- color: item.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
- textFormat: Text.PlainText
- }
-
- LinkedText {
- width: parent.width
- elide: Text.ElideRight
- wrapMode: Text.Wrap
- font.pixelSize: Theme.fontSizeSmall
- shortenUrl: true
- color: item.highlighted ? Theme.highlightColor : Theme.primaryColor
- linkColor: Theme.highlightColor
- plainText: item._bodyText
- }
-
- Row {
- id: metadataRow
-
- width: parent.width
- height: previewRow.visible ? implicitHeight + Theme.paddingMedium : implicitHeight // add padding below
- spacing: Theme.paddingSmall
-
- readonly property color passiveColor: item.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
- readonly property color activeColor: Theme.highlightColor
-
- Label {
- font.pixelSize: Theme.fontSizeExtraSmall
- text: "↩ " + item.commentCount
- color: metadataRow.passiveColor
- }
-
- Label {
- font.pixelSize: Theme.fontSizeExtraSmall
- text: "|"
- color: metadataRow.passiveColor
- }
-
- Label {
- font.pixelSize: Theme.fontSizeExtraSmall
- text: "★ " + (item._likeCountOverride >= 0 ? item._likeCountOverride : item.likeCount)
- color: item.isFavourited ? metadataRow.activeColor : metadataRow.passiveColor
- }
-
- Label {
- font.pixelSize: Theme.fontSizeExtraSmall
- text: "|"
- color: metadataRow.passiveColor
- }
-
- Label {
- font.pixelSize: Theme.fontSizeExtraSmall
- text: "↻ " + (item._boostCountOverride >= 0 ? item._boostCountOverride : item.boostCount)
- color: item.isReblogged ? metadataRow.activeColor : metadataRow.passiveColor
- }
-
- Label {
- visible: item.formattedTime.length > 0
- font.pixelSize: Theme.fontSizeExtraSmall
- text: "|"
- color: metadataRow.passiveColor
- }
-
- Label {
- visible: item.formattedTime.length > 0
- width: Math.max(0, metadataRow.width - x)
- truncationMode: TruncationMode.Fade
- font.pixelSize: Theme.fontSizeExtraSmall
- text: item.formattedTime
- color: metadataRow.passiveColor
- }
- }
-
- SocialMediaPreviewRow {
- id: previewRow
-
- width: parent.width + Theme.horizontalPageMargin // extend to right edge of notification area
- imageList: item.imageList
- downloader: item.downloader
- accountId: item.accountId
- connectedToNetwork: item.connectedToNetwork
- highlighted: item.highlighted
- eventsColumnMaxWidth: item.eventsColumnMaxWidth - item.avatar.width
- }
- }
-
- function stringValue() {
- for (var i = 0; i < arguments.length; ++i) {
- var value = model[arguments[i]]
- if (typeof value === "undefined" || value === null) {
- continue
- }
- value = String(value)
- if (value.length > 0) {
- return value
- }
- }
- return ""
- }
-
- function intValue() {
- for (var i = 0; i < arguments.length; ++i) {
- var value = model[arguments[i]]
- if (typeof value === "undefined" || value === null) {
- continue
- }
- var number = Number(value)
- if (!isNaN(number)) {
- return Math.max(0, Math.floor(number))
- }
- }
- return 0
- }
-
- function actionPostId() {
- if (item.postId.length > 0) {
- return item.postId
- }
- return item.stringValue("mastodonId", "statusId", "id", "twitterId")
- }
-
- function actionAccountId() {
- var parsed = Number(item.accountId)
- return isNaN(parsed) ? -1 : parsed
- }
-
- function shareStatusUrl() {
- return item.stringValue("url", "link", "uri")
- }
-
- function topLevelParent() {
- var p = item
- while (p && p.parent) {
- p = p.parent
- }
- return p
- }
-
- function openActionMenu() {
- if (_actionMenu) {
- _actionMenu.destroy()
- _actionMenu = null
- }
-
- var parentItem = topLevelParent()
- _actionMenu = actionMenuComponent.createObject(parentItem)
- if (_actionMenu) {
- _actionMenu.open(item)
- }
- }
-
- Connections {
- target: item.postActions ? item.postActions : null
-
- onActionSucceeded: {
- if (accountId !== item.actionAccountId() || statusId !== item.actionPostId()) {
- return
- }
-
- if (favouritesCount >= 0) {
- item._likeCountOverride = favouritesCount
- }
- if (reblogsCount >= 0) {
- item._boostCountOverride = reblogsCount
- }
- item._favouritedOverride = favourited ? 1 : 0
- item._rebloggedOverride = reblogged ? 1 : 0
- item._contextMenuOpen = false
-
- if (item._accountDelegate) {
- item._accountDelegate.sync()
- }
- }
-
- onActionFailed: {
- if (accountId !== item.actionAccountId() || statusId !== item.actionPostId()) {
- return
- }
- console.warn("Mastodon action failed:", action, errorMessage)
- item._contextMenuOpen = false
- }
- }
-
- Component {
- id: actionMenuComponent
-
- ContextMenu {
- id: actionMenu
- property bool menuOpen: height > 0
- property bool wasOpened: false
- z: 10000
-
- onPositionChanged: {
- horizontalActions.xPos = _contentColumn.mapFromItem(actionMenu, mouse.x, mouse.y).x
- }
-
- onMenuOpenChanged: {
- if (menuOpen) {
- wasOpened = true
- item._contextMenuOpen = true
- } else if (wasOpened) {
- item._contextMenuOpen = false
- destroy()
- item._actionMenu = null
- }
- }
-
- Item {
- id: horizontalActions
-
- // Makes Silica treat this custom row as a context-menu item.
- property int __silica_menuitem
- property bool down
- property bool highlighted
- signal clicked
-
- property real xPos: 0
- property int hoveredIndex: -1
- readonly property bool actionEnabled: item.postActions
- && item.actionPostId().length > 0
- && item.actionAccountId() >= 0
- && !item.lockScreenActive
- && !item.housekeeping
-
- width: parent.width
- height: Theme.itemSizeMedium
-
- onXPosChanged: hoveredIndex = Math.max(0, Math.min(2, Math.floor((xPos * 3) / Math.max(1, width))))
- onDownChanged: if (!down) hoveredIndex = -1
-
- onClicked: {
- xPos = _contentColumn.mapFromItem(actionMenu, actionMenu.mouseX, actionMenu.mouseY).x
- var index = hoveredIndex >= 0 ? hoveredIndex : Math.max(0, Math.min(2, Math.floor((xPos * 3) / Math.max(1, width))))
- if (!actionEnabled) {
- return
- }
- var postId = item.actionPostId()
- var accountId = item.actionAccountId()
- if (index === 0) {
- if (item.isFavourited) {
- item.postActions.unfavourite(accountId, postId)
- } else {
- item.postActions.favourite(accountId, postId)
- }
- } else if (index === 1) {
- if (item.isReblogged) {
- item.postActions.unboost(accountId, postId)
- } else {
- item.postActions.boost(accountId, postId)
- }
- } else {
- var shareUrl = item.shareStatusUrl()
- if (shareUrl.length === 0) {
- return
- }
- item._shareAction.resources = [{
- "data": shareUrl,
- "linkTitle": item._shareLinkTitle,
- "type": "text/x-url"
- }]
- item._shareAction.trigger()
- }
- }
-
- Rectangle {
- anchors.verticalCenter: parent.verticalCenter
- x: (horizontalActions.hoveredIndex >= 0 ? horizontalActions.hoveredIndex : 0) * (parent.width / 3)
- width: parent.width / 3
- height: parent.height
- visible: horizontalActions.down && horizontalActions.hoveredIndex >= 0
- color: Theme.rgba(Theme.highlightBackgroundColor, Theme.highlightBackgroundOpacity)
- }
-
- Row {
- anchors.fill: parent
-
- Label {
- width: parent.width / 3
- height: parent.height
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- font.pixelSize: Theme.fontSizeExtraLarge
- text: "★"
- color: horizontalActions.actionEnabled
- ? (item.isFavourited
- ? Theme.highlightColor
- : ((horizontalActions.down && horizontalActions.hoveredIndex === 0)
- || (horizontalActions.highlighted && horizontalActions.hoveredIndex === 0)
- ? Theme.secondaryHighlightColor : Theme.primaryColor))
- : Theme.rgba(Theme.secondaryColor, 0.4)
- }
-
- Label {
- width: parent.width / 3
- height: parent.height
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- font.pixelSize: Theme.fontSizeExtraLarge
- text: "↻"
- color: horizontalActions.actionEnabled
- ? (item.isReblogged
- ? Theme.highlightColor
- : ((horizontalActions.down && horizontalActions.hoveredIndex === 1)
- || (horizontalActions.highlighted && horizontalActions.hoveredIndex === 1)
- ? Theme.secondaryHighlightColor : Theme.primaryColor))
- : Theme.rgba(Theme.secondaryColor, 0.4)
- }
-
- Label {
- width: parent.width / 3
- height: parent.height
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- font.pixelSize: Theme.fontSizeExtraLarge
- text: "\u260D"
- color: horizontalActions.actionEnabled
- ? (((horizontalActions.down && horizontalActions.hoveredIndex === 2)
- || (horizontalActions.highlighted && horizontalActions.hoveredIndex === 2))
- ? Theme.secondaryHighlightColor : Theme.primaryColor)
- : Theme.rgba(Theme.secondaryColor, 0.4)
- }
- }
- }
- }
- }
-
- Timer {
- id: openActionMenuTimer
-
- interval: 0
- repeat: false
- onTriggered: {
- if (item.lockScreenActive) {
- item._pendingOpenActionMenu = false
- return
- }
- Lipstick.compositor.eventsLayer.setHousekeeping(false)
- if (item._pendingOpenActionMenu) {
- item._contextMenuOpen = false
- item.openActionMenu()
- }
- item._pendingOpenActionMenu = false
- }
- }
-}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml b/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
deleted file mode 100644
index fac0b89..0000000
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-import QtQuick 2.0
-import Sailfish.Silica 1.0
-import org.nemomobile.socialcache 1.0
-import com.jolla.eventsview.mastodon 1.0
-import QtQml.Models 2.1
-import "shared"
-
-SocialMediaAccountDelegate {
- id: delegateItem
-
- //: Mastodon posts
- //% "Posts"
- headerText: qsTrId("lipstick-jolla-home-la-mastodon_posts")
- headerIcon: "image://theme/icon-l-mastodon"
- showRemainingCount: false
-
- services: ["Posts"]
- socialNetwork: 9
- dataType: SocialSync.Posts
- providerName: "mastodon"
-
- MastodonPostActions {
- id: mastodonPostActions
- }
-
- model: MastodonPostsModel {
- onCountChanged: {
- if (count > 0) {
- if (!updateTimer.running) {
- shortUpdateTimer.start()
- }
- } else {
- shortUpdateTimer.stop()
- }
- }
- }
-
- delegate: MastodonFeedItem {
- downloader: delegateItem.downloader
- imageList: delegateItem.variantRole(model, ["images", "mediaAttachments", "media"])
- avatarSource: delegateItem.convertUrl(delegateItem.stringRole(model, ["icon", "avatar", "avatarUrl"]))
- fallbackAvatarSource: delegateItem.stringRole(model, ["icon", "avatar", "avatarUrl"])
- resolvedStatusUrl: delegateItem.authorizeInteractionUrl(model)
- postId: delegateItem.stringRole(model, ["mastodonId", "statusId", "id", "twitterId"])
- postActions: mastodonPostActions
- accountId: delegateItem.firstAccountId(model)
-
- onTriggered: Qt.openUrlExternally(resolvedStatusUrl)
-
- Component.onCompleted: {
- refreshTimeCount = Qt.binding(function() { return delegateItem.refreshTimeCount })
- connectedToNetwork = Qt.binding(function() { return delegateItem.connectedToNetwork })
- eventsColumnMaxWidth = Qt.binding(function() { return delegateItem.eventsColumnMaxWidth })
- }
- }
- //% "Show more in Mastodon"
- expandedLabel: qsTrId("lipstick-jolla-home-la-show-more-in-mastodon")
-
- onHeaderClicked: Qt.openUrlExternally("https://mastodon.social/explore")
- onExpandedClicked: Qt.openUrlExternally("https://mastodon.social/explore")
-
- onViewVisibleChanged: {
- if (viewVisible) {
- delegateItem.resetHasSyncableAccounts()
- delegateItem.model.refresh()
- if (delegateItem.hasSyncableAccounts && !updateTimer.running) {
- shortUpdateTimer.start()
- }
- } else {
- shortUpdateTimer.stop()
- }
- }
-
- onConnectedToNetworkChanged: {
- if (viewVisible) {
- if (!updateTimer.running) {
- shortUpdateTimer.start()
- }
- }
- }
-
- // The Mastodon feed is updated 3 seconds after the feed view becomes visible,
- // unless it has been updated during last 60 seconds. After that it will be updated
- // periodically in every 60 seconds as long as the feed view is visible.
-
- Timer {
- id: shortUpdateTimer
-
- interval: 3000
- onTriggered: {
- delegateItem.sync()
- updateTimer.start()
- }
- }
-
- Timer {
- id: updateTimer
-
- interval: 60000
- repeat: true
- onTriggered: {
- if (delegateItem.viewVisible) {
- delegateItem.sync()
- } else {
- stop()
- }
- }
- }
-
- function variantRole(modelData, roleNames) {
- for (var i = 0; i < roleNames.length; ++i) {
- var value = modelData[roleNames[i]]
- if (typeof value !== "undefined" && value !== null) {
- return value
- }
- }
- return undefined
- }
-
- function stringRole(modelData, roleNames) {
- for (var i = 0; i < roleNames.length; ++i) {
- var value = modelData[roleNames[i]]
- if (typeof value === "undefined" || value === null) {
- continue
- }
- value = String(value)
- if (value.length > 0) {
- return value
- }
- }
- return ""
- }
-
- function statusUrl(modelData) {
- var directUrl = stringRole(modelData, ["url", "link", "uri"])
- if (directUrl.length > 0) {
- return directUrl
- }
-
- var instanceUrl = stringRole(modelData, ["instanceUrl", "serverUrl", "baseUrl"])
- if (instanceUrl.length === 0) {
- instanceUrl = "https://mastodon.social"
- }
- while (instanceUrl.length > 0 && instanceUrl.charAt(instanceUrl.length - 1) === "/") {
- instanceUrl = instanceUrl.slice(0, instanceUrl.length - 1)
- }
-
- var accountName = stringRole(modelData, ["accountName", "acct", "screenName", "username"])
- var statusId = stringRole(modelData, ["mastodonId", "statusId", "id", "twitterId"])
- if (accountName.length > 0 && statusId.length > 0) {
- while (accountName.length > 0 && accountName.charAt(0) === "@") {
- accountName = accountName.substring(1)
- }
- return instanceUrl + "/@" + accountName + "/" + statusId
- }
-
- return instanceUrl + "/explore"
- }
-
- function authorizeInteractionUrl(modelData) {
- var targetUrl = statusUrl(modelData)
- if (targetUrl.length === 0) {
- return targetUrl
- }
-
- var instanceUrl = stringRole(modelData, ["instanceUrl", "serverUrl", "baseUrl"])
- if (instanceUrl.length === 0) {
- return targetUrl
- }
- while (instanceUrl.length > 0 && instanceUrl.charAt(instanceUrl.length - 1) === "/") {
- instanceUrl = instanceUrl.slice(0, instanceUrl.length - 1)
- }
-
- // Links on the user's own instance should open directly.
- var sameServer = /^([a-z][a-z0-9+.-]*):\/\/([^\/?#]+)/i
- var targetMatch = targetUrl.match(sameServer)
- var instanceMatch = instanceUrl.match(sameServer)
- if (targetMatch && instanceMatch
- && targetMatch.length > 2
- && instanceMatch.length > 2
- && targetMatch[1].toLowerCase() === instanceMatch[1].toLowerCase()
- && targetMatch[2].toLowerCase() === instanceMatch[2].toLowerCase()) {
- return targetUrl
- }
-
- return instanceUrl + "/authorize_interaction?uri=" + encodeURIComponent(targetUrl)
- }
-
- function convertUrl(source) {
- if (source.indexOf("_normal.") !== -1) {
- return source.replace("_normal.", "_bigger.")
- } else if (source.indexOf("_mini.") !== -1) {
- return source.replace("_mini.", "_bigger.")
- }
- return source
- }
-
- function firstAccountId(modelData) {
- var accounts = modelData.accounts
- if (accounts && accounts.length > 0) {
- var accountId = Number(accounts[0])
- if (!isNaN(accountId)) {
- return accountId
- }
- }
- return -1
- }
-}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp b/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp
deleted file mode 100644
index aa98a95..0000000
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2013-2026 Jolla Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "mastodonpostsmodel.h"
-#include <QtCore/QVariantMap>
-
-namespace {
-
-static const char *URL_KEY = "url";
-static const char *TYPE_KEY = "type";
-static const char *TYPE_PHOTO = "photo";
-static const char *TYPE_VIDEO = "video";
-
-QVariantMap createImageData(const SocialPostImage::ConstPtr &image)
-{
- QVariantMap imageData;
- imageData.insert(QLatin1String(URL_KEY), image->url());
- switch (image->type()) {
- case SocialPostImage::Video:
- imageData.insert(QLatin1String(TYPE_KEY), QLatin1String(TYPE_VIDEO));
- break;
- default:
- imageData.insert(QLatin1String(TYPE_KEY), QLatin1String(TYPE_PHOTO));
- break;
- }
- return imageData;
-}
-
-}
-
-MastodonPostsModel::MastodonPostsModel(QObject *parent)
- : QAbstractListModel(parent)
-{
- connect(&m_database, &AbstractSocialPostCacheDatabase::postsChanged,
- this, &MastodonPostsModel::postsChanged);
- connect(&m_database, SIGNAL(accountIdFilterChanged()),
- this, SIGNAL(accountIdFilterChanged()));
-}
-
-int MastodonPostsModel::rowCount(const QModelIndex &parent) const
-{
- Q_UNUSED(parent)
- return m_data.count();
-}
-
-QVariant MastodonPostsModel::data(const QModelIndex &index, int role) const
-{
- const int row = index.row();
- if (!index.isValid() || row < 0 || row >= m_data.count()) {
- return QVariant();
- }
-
- return m_data.at(row).value(role);
-}
-
-QHash<int, QByteArray> MastodonPostsModel::roleNames() const
-{
- QHash<int, QByteArray> roleNames;
- roleNames.insert(MastodonId, "mastodonId");
- roleNames.insert(Name, "name");
- roleNames.insert(AccountName, "accountName");
- roleNames.insert(Acct, "acct");
- roleNames.insert(Body, "body");
- roleNames.insert(Timestamp, "timestamp");
- roleNames.insert(Icon, "icon");
- roleNames.insert(Images, "images");
- roleNames.insert(Url, "url");
- roleNames.insert(Link, "link");
- roleNames.insert(BoostedBy, "boostedBy");
- roleNames.insert(RebloggedBy, "rebloggedBy");
- roleNames.insert(RepliesCount, "repliesCount");
- roleNames.insert(FavouritesCount, "favouritesCount");
- roleNames.insert(ReblogsCount, "reblogsCount");
- roleNames.insert(Favourited, "favourited");
- roleNames.insert(Reblogged, "reblogged");
- roleNames.insert(InstanceUrl, "instanceUrl");
- roleNames.insert(Accounts, "accounts");
- return roleNames;
-}
-
-QVariantList MastodonPostsModel::accountIdFilter() const
-{
- return m_database.accountIdFilter();
-}
-
-void MastodonPostsModel::setAccountIdFilter(const QVariantList &accountIds)
-{
- m_database.setAccountIdFilter(accountIds);
-}
-
-void MastodonPostsModel::refresh()
-{
- m_database.refresh();
-}
-
-void MastodonPostsModel::postsChanged()
-{
- QList<RowData> data;
- QList<SocialPost::ConstPtr> postsData = m_database.posts();
- Q_FOREACH (const SocialPost::ConstPtr &post, postsData) {
- RowData eventMap;
- const QString accountName = m_database.accountName(post);
- const QString postUrl = m_database.url(post);
- const QString boostedBy = m_database.boostedBy(post);
- const int repliesCount = m_database.repliesCount(post);
- const int favouritesCount = m_database.favouritesCount(post);
- const int reblogsCount = m_database.reblogsCount(post);
- const bool favourited = m_database.favourited(post);
- const bool reblogged = m_database.reblogged(post);
-
- eventMap.insert(MastodonPostsModel::MastodonId, post->identifier());
- eventMap.insert(MastodonPostsModel::Name, post->name());
- eventMap.insert(MastodonPostsModel::AccountName, accountName);
- eventMap.insert(MastodonPostsModel::Acct, accountName);
- eventMap.insert(MastodonPostsModel::Body, post->body());
- eventMap.insert(MastodonPostsModel::Timestamp, post->timestamp());
- eventMap.insert(MastodonPostsModel::Icon, post->icon());
- eventMap.insert(MastodonPostsModel::Url, postUrl);
- eventMap.insert(MastodonPostsModel::Link, postUrl);
- eventMap.insert(MastodonPostsModel::BoostedBy, boostedBy);
- eventMap.insert(MastodonPostsModel::RebloggedBy, boostedBy);
- eventMap.insert(MastodonPostsModel::RepliesCount, repliesCount);
- eventMap.insert(MastodonPostsModel::FavouritesCount, favouritesCount);
- eventMap.insert(MastodonPostsModel::ReblogsCount, reblogsCount);
- eventMap.insert(MastodonPostsModel::Favourited, favourited);
- eventMap.insert(MastodonPostsModel::Reblogged, reblogged);
- eventMap.insert(MastodonPostsModel::InstanceUrl, m_database.instanceUrl(post));
-
- QVariantList images;
- Q_FOREACH (const SocialPostImage::ConstPtr &image, post->images()) {
- images.append(createImageData(image));
- }
- eventMap.insert(MastodonPostsModel::Images, images);
-
- QVariantList accountsVariant;
- Q_FOREACH (int account, post->accounts()) {
- accountsVariant.append(account);
- }
- eventMap.insert(MastodonPostsModel::Accounts, accountsVariant);
- data.append(eventMap);
- }
-
- const int oldCount = m_data.count();
- beginResetModel();
- m_data = data;
- endResetModel();
- if (oldCount != m_data.count()) {
- emit countChanged();
- }
-}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp b/eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp
deleted file mode 100644
index 9ade6e2..0000000
--- a/eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <QQmlExtensionPlugin>
-#include <QtQml>
-
-#include "mastodonpostactions.h"
-#include "mastodonpostsmodel.h"
-
-class JollaEventsviewMastodonPlugin : public QQmlExtensionPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID "com.jolla.eventsview.mastodon")
-
-public:
- void registerTypes(const char *uri) override
- {
- Q_ASSERT(QLatin1String(uri) == QLatin1String("com.jolla.eventsview.mastodon"));
- qmlRegisterType<MastodonPostsModel>(uri, 1, 0, "MastodonPostsModel");
- qmlRegisterType<MastodonPostActions>(uri, 1, 0, "MastodonPostActions");
- }
-};
-
-#include "plugin.moc"
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/qmldir b/eventsview-plugins/eventsview-plugin-mastodon/qmldir
deleted file mode 100644
index 74461ce..0000000
--- a/eventsview-plugins/eventsview-plugin-mastodon/qmldir
+++ /dev/null
@@ -1,4 +0,0 @@
-# Copyright (C) 2013-2026 Jolla Ltd.
-
-module com.jolla.eventsview.mastodon
-plugin jollaeventsviewmastodonplugin
diff --git a/eventsview-plugins/eventsview-plugins.pro b/eventsview-plugins/eventsview-plugins.pro
index 095fd02..bc96c5f 100644
--- a/eventsview-plugins/eventsview-plugins.pro
+++ b/eventsview-plugins/eventsview-plugins.pro
@@ -3,4 +3,4 @@
# SPDX-License-Identifier: BSD-3-Clause
TEMPLATE = subdirs
-SUBDIRS += eventsview-plugin-mastodon
+SUBDIRS += eventsview-plugin-fediverse