summaryrefslogtreecommitdiff
path: root/eventsview-plugins/eventsview-plugin-mastodon
diff options
context:
space:
mode:
Diffstat (limited to 'eventsview-plugins/eventsview-plugin-mastodon')
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml143
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.cpp190
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.h72
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel_p.h53
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro61
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon.ts24
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon_eng_en.qmbin0 -> 299 bytes
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml167
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp132
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.h67
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp19
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/postimagehelper_p.h45
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/qmldir2
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/synchronizelists_p.h224
14 files changed, 1199 insertions, 0 deletions
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml b/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml
new file mode 100644
index 0000000..231b814
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml
@@ -0,0 +1,143 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2026 Open Mobile Platform LLC.
+ **
+ ****************************************************************************/
+
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import Sailfish.TextLinking 1.0
+import "shared"
+
+SocialMediaFeedItem {
+ id: item
+
+ property variant imageList
+ property int likeCount
+ property int commentCount
+ property int boostCount
+
+ property string _booster: item.stringValue("boostedBy", "rebloggedBy", "retweeter")
+ property string _displayName: item.stringValue("name", "displayName", "display_name")
+ property string _accountName: item.stringValue("accountName", "acct", "screenName", "username")
+ property string _bodyText: item.stringValue("body", "content", "text")
+
+ timestamp: item.stringValue("timestamp", "createdAt", "created_at")
+
+ avatar.y: item._booster.length > 0
+ ? topMargin + boosterIcon.height + Theme.paddingSmall
+ : topMargin
+ contentHeight: Math.max(content.y + content.height, avatar.y + avatar.height) + bottomMargin
+ 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
+ }
+
+ Text {
+ width: parent.width
+ height: previewRow.visible ? implicitHeight + Theme.paddingMedium : implicitHeight // add padding below
+ maximumLineCount: 1
+ elide: Text.ElideRight
+ wrapMode: Text.Wrap
+ color: item.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ font.pixelSize: Theme.fontSizeExtraSmall
+ text: item.formattedTime
+ textFormat: Text.PlainText
+ }
+
+ 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 ""
+ }
+}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.cpp b/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.cpp
new file mode 100644
index 0000000..6d33d48
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2013 Jolla Ltd.
+ * Contact: Lucien Xu <lucien.xu@jollamobile.com>
+ *
+ * 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 "abstractsocialcachemodel.h"
+#include "abstractsocialcachemodel_p.h"
+
+#include <synchronizelists_p.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QMutexLocker>
+
+template <> bool compareIdentity<SocialCacheModelRow>(
+ const SocialCacheModelRow &item, const SocialCacheModelRow &reference)
+{
+ return item.value(0) == reference.value(0);
+}
+
+template <>
+int updateRange<AbstractSocialCacheModelPrivate, SocialCacheModelData>(
+ AbstractSocialCacheModelPrivate *d,
+ int index,
+ int count,
+ const SocialCacheModelData &source,
+ int sourceIndex)
+{
+ d->updateRange(index, count, source, sourceIndex);
+
+ return count;
+}
+
+AbstractSocialCacheModelPrivate::AbstractSocialCacheModelPrivate(AbstractSocialCacheModel *q)
+ : q_ptr(q)
+{
+}
+
+AbstractSocialCacheModelPrivate::~AbstractSocialCacheModelPrivate()
+{
+}
+
+void AbstractSocialCacheModelPrivate::clearData()
+{
+ Q_Q(AbstractSocialCacheModel);
+ if (m_data.count() > 0) {
+ q->beginRemoveRows(QModelIndex(), 0, m_data.count() - 1);
+ m_data.clear();
+ q->endRemoveRows();
+ emit q->countChanged();
+ }
+}
+
+void AbstractSocialCacheModelPrivate::updateData(const SocialCacheModelData &data)
+{
+ Q_Q(AbstractSocialCacheModel);
+ q->updateData(data);
+}
+
+void AbstractSocialCacheModelPrivate::updateRow(int row, const SocialCacheModelRow &data)
+{
+ Q_Q(AbstractSocialCacheModel);
+ q->updateRow(row, data);
+}
+
+void AbstractSocialCacheModelPrivate::insertRange(
+ int index, int count, const SocialCacheModelData &source, int sourceIndex)
+{
+ Q_Q(AbstractSocialCacheModel);
+
+ if (count > 0 && index >= 0) {
+ q->beginInsertRows(QModelIndex(), index, index + count - 1);
+ m_data = m_data.mid(0, index) + source.mid(sourceIndex, count) + m_data.mid(index);
+ q->endInsertRows();
+ emit q->countChanged();
+ }
+}
+
+void AbstractSocialCacheModelPrivate::removeRange(int index, int count)
+{
+ Q_Q(AbstractSocialCacheModel);
+
+ if (count > 0 && index >= 0) {
+ q->beginRemoveRows(QModelIndex(), index, index + count - 1);
+ m_data = m_data.mid(0, index) + m_data.mid(index + count);
+ q->endRemoveRows();
+ emit q->countChanged();
+ }
+}
+
+void AbstractSocialCacheModelPrivate::updateRange(
+ int index, int count, const SocialCacheModelData &source, int sourceIndex)
+{
+ Q_Q(AbstractSocialCacheModel);
+
+ for (int i = 0; i < count; ++i) {
+ m_data[index + i] = source[sourceIndex + i];
+ }
+
+ emit q->dataChanged(q->createIndex(index, 0), q->createIndex(index + count - 1, 0));
+}
+
+AbstractSocialCacheModel::AbstractSocialCacheModel(AbstractSocialCacheModelPrivate &dd,
+ QObject *parent)
+ : QAbstractListModel(parent), d_ptr(&dd)
+{
+}
+
+AbstractSocialCacheModel::~AbstractSocialCacheModel()
+{
+}
+
+int AbstractSocialCacheModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ Q_D(const AbstractSocialCacheModel);
+ return d->m_data.count();
+}
+
+QVariant AbstractSocialCacheModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ return getField(row, role);
+}
+
+QVariant AbstractSocialCacheModel::getField(int row, int role) const
+{
+ Q_D(const AbstractSocialCacheModel);
+ if (row < 0 || row >= d->m_data.count()) {
+ return QVariant();
+ }
+
+ return d->m_data.at(row).value(role);
+}
+
+QString AbstractSocialCacheModel::nodeIdentifier() const
+{
+ Q_D(const AbstractSocialCacheModel);
+ return d->nodeIdentifier;
+}
+
+void AbstractSocialCacheModel::setNodeIdentifier(const QString &nodeIdentifier)
+{
+ Q_D(AbstractSocialCacheModel);
+ if (d->nodeIdentifier != nodeIdentifier) {
+ d->nodeIdentifier = nodeIdentifier;
+ emit nodeIdentifierChanged();
+ d->nodeIdentifierChanged();
+ }
+}
+
+int AbstractSocialCacheModel::count() const
+{
+ return rowCount();
+}
+
+void AbstractSocialCacheModel::updateData(const SocialCacheModelData &data)
+{
+ Q_D(AbstractSocialCacheModel);
+
+ const int count = d->m_data.count();
+ synchronizeList(d, d->m_data, data);
+
+ if (d->m_data.count() != count) {
+ emit countChanged();
+ }
+ emit modelUpdated();
+}
+
+void AbstractSocialCacheModel::updateRow(int row, const SocialCacheModelRow &data)
+{
+ Q_D(AbstractSocialCacheModel);
+ foreach (int key, data.keys()) {
+ d->m_data[row].insert(key, data.value(key));
+ }
+ emit dataChanged(index(row), index(row));
+}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.h b/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.h
new file mode 100644
index 0000000..1e6394f
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 Jolla Ltd.
+ * Contact: Lucien Xu <lucien.xu@jollamobile.com>
+ *
+ * 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
+ */
+
+#ifndef ABSTRACTSOCIALCACHEMODEL_H
+#define ABSTRACTSOCIALCACHEMODEL_H
+
+#include <QtCore/QAbstractListModel>
+
+typedef QMap<int, QVariant> SocialCacheModelRow;
+typedef QList<SocialCacheModelRow> SocialCacheModelData;
+
+class AbstractSocialCacheModelPrivate;
+
+class AbstractSocialCacheModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QString nodeIdentifier READ nodeIdentifier WRITE setNodeIdentifier NOTIFY nodeIdentifierChanged)
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+
+public:
+ virtual ~AbstractSocialCacheModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ Q_INVOKABLE QVariant getField(int row, int role) const;
+
+ // properties
+ QString nodeIdentifier() const;
+ void setNodeIdentifier(const QString &nodeIdentifier);
+ int count() const;
+
+
+public Q_SLOTS:
+ virtual void refresh() = 0;
+
+Q_SIGNALS:
+ void nodeIdentifierChanged();
+ void countChanged();
+ void modelUpdated();
+
+protected:
+ // Methods used to update the model in the C++ side
+ void updateData(const SocialCacheModelData &data);
+ void updateRow(int row, const SocialCacheModelRow &data);
+
+ explicit AbstractSocialCacheModel(AbstractSocialCacheModelPrivate &dd, QObject *parent = 0);
+ QScopedPointer<AbstractSocialCacheModelPrivate> d_ptr;
+
+private:
+ Q_DECLARE_PRIVATE(AbstractSocialCacheModel)
+};
+
+Q_DECLARE_METATYPE(SocialCacheModelRow)
+Q_DECLARE_METATYPE(SocialCacheModelData)
+
+#endif // ABSTRACTSOCIALCACHEMODEL_H
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel_p.h b/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel_p.h
new file mode 100644
index 0000000..6c92655
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/abstractsocialcachemodel_p.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 Jolla Ltd.
+ * Contact: Lucien Xu <lucien.xu@jollamobile.com>
+ *
+ * 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
+ */
+
+#ifndef ABSTRACTSOCIALCACHEMODEL_P_H
+#define ABSTRACTSOCIALCACHEMODEL_P_H
+
+#include "abstractsocialcachemodel.h"
+
+#include <QtCore/QMap>
+
+class AbstractSocialCacheModelPrivate
+{
+public:
+ virtual ~AbstractSocialCacheModelPrivate();
+ QString nodeIdentifier;
+
+ void insertRange(int index, int count, const SocialCacheModelData &source, int sourceIndex);
+ void updateRange(int index, int count, const SocialCacheModelData &source, int sourceIndex);
+ void removeRange(int index, int count);
+
+ void clearData();
+ void updateData(const SocialCacheModelData &data);
+ void updateRow(int row, const SocialCacheModelRow &data);
+
+ QList<QMap<int, QVariant> > m_data;
+
+protected:
+ explicit AbstractSocialCacheModelPrivate(AbstractSocialCacheModel *q);
+
+ virtual void nodeIdentifierChanged() {}
+
+ AbstractSocialCacheModel * const q_ptr;
+private:
+ Q_DECLARE_PUBLIC(AbstractSocialCacheModel)
+};
+
+#endif // ABSTRACTSOCIALCACHEMODEL_P_H
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro b/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro
new file mode 100644
index 0000000..229f38a
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro
@@ -0,0 +1,61 @@
+TEMPLATE = lib
+TARGET = jollaeventsviewmastodonplugin
+TARGET = $$qtLibraryTarget($$TARGET)
+
+MODULENAME = com/jolla/eventsview/mastodon
+TARGETPATH = $$[QT_INSTALL_QML]/$$MODULENAME
+
+QT += qml
+CONFIG += plugin link_pkgconfig
+PKGCONFIG += socialcache
+
+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.commands += lupdate $$PWD -ts $$TS_FILE
+ts.CONFIG += no_check_exist no_link
+ts.output = $$TS_FILE
+ts.input = .
+
+ts_install.files = $$TS_FILE
+ts_install.path = /usr/share/translations/source
+ts_install.CONFIG += no_check_exist
+
+# should add -markuntranslated "-" when proper translations are in place (or for testing)
+engineering_english.commands += lrelease -idbased $$TS_FILE -qm $$EE_QM
+engineering_english.CONFIG += no_check_exist no_link
+engineering_english.depends = ts
+engineering_english.input = $$TS_FILE
+engineering_english.output = $$EE_QM
+
+engineering_english_install.path = /usr/share/translations
+engineering_english_install.files = $$EE_QM
+engineering_english_install.CONFIG += no_check_exist
+
+QMAKE_EXTRA_TARGETS += ts engineering_english
+PRE_TARGETDEPS += ts engineering_english
+
+INSTALLS += ts_install engineering_english_install
+
+HEADERS += \
+ abstractsocialcachemodel.h \
+ abstractsocialcachemodel_p.h \
+ mastodonpostsmodel.h
+
+SOURCES += \
+ abstractsocialcachemodel.cpp \
+ mastodonpostsmodel.cpp \
+ plugin.cpp
+
+qml.files = mastodon-delegate.qml MastodonFeedItem.qml
+qml.path = /usr/share/lipstick/eventfeed/
+
+import.files = qmldir
+import.path = $$TARGETPATH
+target.path = $$TARGETPATH
+
+OTHER_FILES += $$qml.files $$import.files
+
+INSTALLS += target import qml
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon.ts b/eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon.ts
new file mode 100644
index 0000000..60f39fa
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon.ts
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name></name>
+ <message id="lipstick-jolla-home-la-mastodon_posts">
+ <location filename="mastodon-delegate.qml" line="19"/>
+ <source>Posts</source>
+ <extracomment>Mastodon posts</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message id="lipstick-jolla-home-la-show-more-in-mastodon">
+ <location filename="mastodon-delegate.qml" line="56"/>
+ <source>Show more in Mastodon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message id="lipstick-jolla-home-la-boosted_by">
+ <location filename="MastodonFeedItem.qml" line="61"/>
+ <source>%1 boosted</source>
+ <extracomment>Shown above a post that is boosted by another user. %1 = name of user who boosted</extracomment>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon_eng_en.qm b/eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon_eng_en.qm
new file mode 100644
index 0000000..30da83b
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/lipstick-jolla-home-mastodon_eng_en.qm
Binary files differ
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml b/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
new file mode 100644
index 0000000..ed79fdb
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
@@ -0,0 +1,167 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2026 Open Mobile Platform LLC.
+ **
+ ****************************************************************************/
+
+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/graphic-service-mastodon"
+ showRemainingCount: false
+
+ services: ["Posts", "Notifications"]
+ socialNetwork: 9
+ dataType: SocialSync.Posts
+ providerName: "mastodon"
+
+ 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"])
+ accountId: model.accounts[0]
+
+ onTriggered: Qt.openUrlExternally(delegateItem.statusUrl(model))
+
+ 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 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
+ }
+}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp b/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp
new file mode 100644
index 0000000..3e54b8b
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2026 Open Mobile Platform LLC.
+ *
+ * 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 "abstractsocialcachemodel_p.h"
+#include "mastodonpostsdatabase.h"
+#include "postimagehelper_p.h"
+
+class MastodonPostsModelPrivate: public AbstractSocialCacheModelPrivate
+{
+public:
+ explicit MastodonPostsModelPrivate(MastodonPostsModel *q);
+
+ MastodonPostsDatabase database;
+
+private:
+ Q_DECLARE_PUBLIC(MastodonPostsModel)
+};
+
+MastodonPostsModelPrivate::MastodonPostsModelPrivate(MastodonPostsModel *q)
+ : AbstractSocialCacheModelPrivate(q)
+{
+}
+
+MastodonPostsModel::MastodonPostsModel(QObject *parent)
+ : AbstractSocialCacheModel(*(new MastodonPostsModelPrivate(this)), parent)
+{
+ Q_D(MastodonPostsModel);
+
+ connect(&d->database, &AbstractSocialPostCacheDatabase::postsChanged,
+ this, &MastodonPostsModel::postsChanged);
+ connect(&d->database, SIGNAL(accountIdFilterChanged()),
+ this, SIGNAL(accountIdFilterChanged()));
+}
+
+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(InstanceUrl, "instanceUrl");
+ roleNames.insert(Accounts, "accounts");
+ return roleNames;
+}
+
+QVariantList MastodonPostsModel::accountIdFilter() const
+{
+ Q_D(const MastodonPostsModel);
+
+ return d->database.accountIdFilter();
+}
+
+void MastodonPostsModel::setAccountIdFilter(const QVariantList &accountIds)
+{
+ Q_D(MastodonPostsModel);
+
+ d->database.setAccountIdFilter(accountIds);
+}
+
+void MastodonPostsModel::refresh()
+{
+ Q_D(MastodonPostsModel);
+
+ d->database.refresh();
+}
+
+void MastodonPostsModel::postsChanged()
+{
+ Q_D(MastodonPostsModel);
+
+ SocialCacheModelData data;
+ QList<SocialPost::ConstPtr> postsData = d->database.posts();
+ Q_FOREACH (const SocialPost::ConstPtr &post, postsData) {
+ QMap<int, QVariant> eventMap;
+ const QString accountName = d->database.accountName(post);
+ const QString postUrl = d->database.url(post);
+ const QString boostedBy = d->database.boostedBy(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::InstanceUrl, d->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);
+ }
+
+ updateData(data);
+}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.h b/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.h
new file mode 100644
index 0000000..9692729
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2026 Open Mobile Platform LLC.
+ *
+ * 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
+ */
+
+#ifndef MASTODONPOSTSMODEL_H
+#define MASTODONPOSTSMODEL_H
+
+#include "abstractsocialcachemodel.h"
+
+class MastodonPostsModelPrivate;
+
+class MastodonPostsModel: public AbstractSocialCacheModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariantList accountIdFilter READ accountIdFilter WRITE setAccountIdFilter NOTIFY accountIdFilterChanged)
+
+public:
+ enum MastodonPostsRole {
+ MastodonId = 0,
+ Name,
+ AccountName,
+ Acct,
+ Body,
+ Timestamp,
+ Icon,
+ Images,
+ Url,
+ Link,
+ BoostedBy,
+ RebloggedBy,
+ InstanceUrl,
+ Accounts
+ };
+
+ explicit MastodonPostsModel(QObject *parent = 0);
+ QHash<int, QByteArray> roleNames() const;
+
+ QVariantList accountIdFilter() const;
+ void setAccountIdFilter(const QVariantList &accountIds);
+
+ void refresh();
+
+signals:
+ void accountIdFilterChanged();
+
+private slots:
+ void postsChanged();
+
+private:
+ Q_DECLARE_PRIVATE(MastodonPostsModel)
+};
+
+#endif // MASTODONPOSTSMODEL_H
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp b/eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp
new file mode 100644
index 0000000..35d95ca
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/plugin.cpp
@@ -0,0 +1,19 @@
+#include <QQmlExtensionPlugin>
+#include <QtQml>
+
+#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");
+ }
+};
+
+#include "plugin.moc"
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/postimagehelper_p.h b/eventsview-plugins/eventsview-plugin-mastodon/postimagehelper_p.h
new file mode 100644
index 0000000..fe61212
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/postimagehelper_p.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 Jolla Ltd.
+ * Contact: Lucien Xu <lucien.xu@jollamobile.com>
+ *
+ * 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
+ */
+
+#ifndef POSTIMAGEHELPER_P_H
+#define POSTIMAGEHELPER_P_H
+
+#include <QtCore/QVariantMap>
+
+static const char *URL_KEY = "url";
+static const char *TYPE_KEY = "type";
+static const char *TYPE_PHOTO = "photo";
+static const char *TYPE_VIDEO = "video";
+
+inline static 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;
+}
+
+#endif // POSTIMAGEHELPER_P_H
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/qmldir b/eventsview-plugins/eventsview-plugin-mastodon/qmldir
new file mode 100644
index 0000000..515a0a0
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/qmldir
@@ -0,0 +1,2 @@
+module com.jolla.eventsview.mastodon
+plugin jollaeventsviewmastodonplugin
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/synchronizelists_p.h b/eventsview-plugins/eventsview-plugin-mastodon/synchronizelists_p.h
new file mode 100644
index 0000000..1e09e86
--- /dev/null
+++ b/eventsview-plugins/eventsview-plugin-mastodon/synchronizelists_p.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2013 Jolla Mobile <andrew.den.exter@jollamobile.com>
+ *
+ * You may use this file under the terms of the BSD license as follows:
+ *
+ * "Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Nemo Mobile nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ */
+
+#ifndef SYNCHRONIZELISTS_P_H
+#define SYNCHRONIZELISTS_P_H
+
+template <typename T>
+bool compareIdentity(const T &item, const T &reference)
+{
+ return item == reference;
+}
+
+template <typename Agent, typename ReferenceList>
+int insertRange(Agent *agent, int index, int count, const ReferenceList &source, int sourceIndex)
+{
+ agent->insertRange(index, count, source, sourceIndex);
+ return count;
+}
+
+template <typename Agent>
+int removeRange(Agent *agent, int index, int count)
+{
+ agent->removeRange(index, count);
+ return 0;
+}
+
+template <typename Agent, typename ReferenceList>
+int updateRange(Agent *agent, int index, int count, const ReferenceList &source, int sourceIndex)
+{
+ Q_UNUSED(agent);
+ Q_UNUSED(index);
+ Q_UNUSED(source);
+ Q_UNUSED(sourceIndex);
+ return count;
+}
+
+template <typename Agent, typename CacheList, typename ReferenceList>
+class SynchronizeList
+{
+public:
+ SynchronizeList(
+ Agent *agent,
+ const CacheList &cache,
+ int &c,
+ const ReferenceList &reference,
+ int &r)
+ : agent(agent), cache(cache), c(c), reference(reference), r(r)
+ {
+ int lastEqualC = c;
+ int lastEqualR = r;
+ for (; c < cache.count() && r < reference.count(); ++c, ++r) {
+ if (compareIdentity(cache.at(c), reference.at(r))) {
+ continue;
+ }
+
+ if (c > lastEqualC) {
+ lastEqualC += updateRange(agent, lastEqualC, c - lastEqualC, reference, lastEqualR);
+ c = lastEqualC;
+ lastEqualR = r;
+ }
+
+ bool match = false;
+
+ // Iterate through both the reference and cache lists in parallel looking for first
+ // point of commonality, when that is found resolve the differences and continue
+ // looking.
+ int count = 1;
+ for (; !match && c + count < cache.count() && r + count < reference.count(); ++count) {
+ typename CacheList::const_reference cacheItem = cache.at(c + count);
+ typename ReferenceList::const_reference referenceItem = reference.at(r + count);
+
+ for (int i = 0; i <= count; ++i) {
+ if (cacheMatch(i, count, referenceItem) || referenceMatch(i, count, cacheItem)) {
+ match = true;
+ break;
+ }
+ }
+ }
+
+ // Continue scanning the reference list if the cache has been exhausted.
+ for (int re = r + count; !match && re < reference.count(); ++re) {
+ typename ReferenceList::const_reference referenceItem = reference.at(re);
+ for (int i = 0; i < count; ++i) {
+ if (cacheMatch(i, re - r, referenceItem)) {
+ match = true;
+ break;
+ }
+ }
+ }
+
+ // Continue scanning the cache if the reference list has been exhausted.
+ for (int ce = c + count; !match && ce < cache.count(); ++ce) {
+ typename CacheList::const_reference cacheItem = cache.at(ce);
+ for (int i = 0; i < count; ++i) {
+ if (referenceMatch(i, ce - c, cacheItem)) {
+ match = true;
+ break;
+ }
+ }
+ }
+
+ if (!match)
+ return;
+
+ lastEqualC = c;
+ lastEqualR = r;
+ }
+
+ if (c > lastEqualC) {
+ updateRange(agent, lastEqualC, c - lastEqualC, reference, lastEqualR);
+ }
+ }
+
+private:
+ // Tests if the cached contact id at i matches a referenceId.
+ // If there is a match removes all items traversed in the cache since the previous match
+ // and inserts any items in the reference set found to to not be in the cache.
+ bool cacheMatch(int i, int count, typename ReferenceList::const_reference referenceItem)
+ {
+ if (compareIdentity(cache.at(c + i), referenceItem)) {
+ if (i > 0)
+ c += removeRange(agent, c, i);
+ c += insertRange(agent, c, count, reference, r);
+ r += count;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Tests if the reference contact id at i matches a cacheId.
+ // If there is a match inserts all items traversed in the reference set since the
+ // previous match and removes any items from the cache that were not found in the
+ // reference list.
+ bool referenceMatch(int i, int count, typename ReferenceList::const_reference cacheItem)
+ {
+ if (compareIdentity(reference.at(r + i), cacheItem)) {
+ c += removeRange(agent, c, count);
+ if (i > 0)
+ c += insertRange(agent, c, i, reference, r);
+ r += i;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ Agent * const agent;
+ const CacheList &cache;
+ int &c;
+ const ReferenceList &reference;
+ int &r;
+};
+
+template <typename Agent, typename CacheList, typename ReferenceList>
+void completeSynchronizeList(
+ Agent *agent,
+ const CacheList &cache,
+ int &cacheIndex,
+ const ReferenceList &reference,
+ int &referenceIndex)
+{
+ if (cacheIndex < cache.count()) {
+ agent->removeRange(cacheIndex, cache.count() - cacheIndex);
+ }
+ if (referenceIndex < reference.count()) {
+ agent->insertRange(cache.count(), reference.count() - referenceIndex, reference, referenceIndex);
+ }
+
+ cacheIndex = 0;
+ referenceIndex = 0;
+}
+
+template <typename Agent, typename CacheList, typename ReferenceList>
+void synchronizeList(
+ Agent *agent,
+ const CacheList &cache,
+ int &cacheIndex,
+ const ReferenceList &reference,
+ int &referenceIndex)
+{
+ SynchronizeList<Agent, CacheList, ReferenceList>(
+ agent, cache, cacheIndex, reference, referenceIndex);
+}
+
+template <typename Agent, typename CacheList, typename ReferenceList>
+void synchronizeList(Agent *agent, const CacheList &cache, const ReferenceList &reference)
+{
+ int cacheIndex = 0;
+ int referenceIndex = 0;
+ SynchronizeList<Agent, CacheList, ReferenceList>(
+ agent, cache, cacheIndex, reference, referenceIndex);
+ completeSynchronizeList(agent, cache, cacheIndex, reference, referenceIndex);
+}
+
+#endif