summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Branson <andrew.branson@jolla.com>2026-03-22 20:18:39 +0100
committerAndrew Branson <andrew.branson@jolla.com>2026-03-22 20:25:03 +0100
commit218a05f6ac67f260288ff70344f0f004c7b48c7b (patch)
treed213c375b94b467aa439aabf9810554087b29ec0
parent2b1a3046832074e47ad2ad703cd518526b9fb459 (diff)
Use shared buteo-common and split notifications service on mainmain
Keep main as the branch that builds against the newer shared social sync modules, while master stays self-contained. - drop the bundled buteo-common sources and stop building or packaging libmastodonbuteocommon - link the Mastodon sync plugins against buteosocialcommon and add the matching build/runtime package requirements - install a dedicated mastodon-notifications account service and wire account creation/packaging around the separate notifications profile - move the posts/events-view side over to the newer shared-helper style used with the updated socialcache stack - clean up qmake project wiring for the shared-module layout and refresh the branch README to describe the split service model - keep the notification schedule at the master value instead of carrying the temporary timing tweak
-rw-r--r--README.md9
-rw-r--r--buteo-plugins/buteo-common/buteo-common.pri13
-rw-r--r--buteo-plugins/buteo-common/buteo-common.pro33
-rw-r--r--buteo-plugins/buteo-common/buteosyncfw_p.h39
-rw-r--r--buteo-plugins/buteo-common/socialdbuteoplugin.cpp355
-rw-r--r--buteo-plugins/buteo-common/socialdbuteoplugin.h75
-rw-r--r--buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp37
-rw-r--r--buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h40
-rw-r--r--buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp470
-rw-r--r--buteo-plugins/buteo-common/socialnetworksyncadaptor.h155
-rw-r--r--buteo-plugins/buteo-common/trace.cpp25
-rw-r--r--buteo-plugins/buteo-common/trace.h30
-rw-r--r--buteo-plugins/buteo-plugins.pro4
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-notifications/buteo-sync-plugin-mastodon-notifications.pro10
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-posts/buteo-sync-plugin-mastodon-posts.pro10
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp24
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodonpostssyncadaptor.cpp12
-rw-r--r--common/mastodonpostsdatabase.cpp46
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml318
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro1
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml170
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp51
-rw-r--r--rpm/sailfish-account-mastodon.spec6
-rw-r--r--settings/accounts-translations-plugin/accounts-translations-plugin.pro1
-rw-r--r--transferengine-plugins/mastodonshareplugin/mastodonshareplugin.pro2
-rw-r--r--transferengine-plugins/mastodontransferplugin/mastodontransferplugin.pro1
26 files changed, 229 insertions, 1708 deletions
diff --git a/README.md b/README.md
index 6ce2dfb..e677094 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,8 @@ Sailfish OS account integration for Mastodon.
- Translation source catalog: `/usr/share/translations/source/settings-accounts-mastodon.ts`
- Provider/service metadata uses `<translations>/usr/share/translations/settings-accounts-mastodon</translations>` for metadata string translation paths.
- Services:
- - `mastodon-microblog`: sync service for posts and notifications.
+ - `mastodon-microblog`: sync service for posts.
+ - `mastodon-notifications`: sync service for notifications.
- `mastodon-sharing`: Transfer Engine sharing service.
### `buteo-plugins/`
@@ -80,10 +81,4 @@ Required SDK-provided dependencies include (not exhaustive):
- `nemotransferengine-qt5`
- related Qt/account stack packages listed in `rpm/sailfish-account-mastodon.spec`
-## Typical Build Flow (Inside Sailfish SDK)
-
-1. Enter Sailfish SDK shell/target.
-2. Build from repository root (`qmake` / `make`).
-3. Build RPM package(s) from `rpm/sailfish-account-mastodon.spec`.
-
Outside Sailfish SDK, only static validation (wiring, paths, spec consistency) should be considered reliable.
diff --git a/buteo-plugins/buteo-common/buteo-common.pri b/buteo-plugins/buteo-common/buteo-common.pri
deleted file mode 100644
index 83452ac..0000000
--- a/buteo-plugins/buteo-common/buteo-common.pri
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-
-INCLUDEPATH += $$PWD
-DEPENDPATH += .
-
-QT += dbus
-
-CONFIG += link_pkgconfig
-PKGCONFIG += accounts-qt5 buteosyncfw5 socialcache libsignon-qt5 libsailfishkeyprovider
-
-LIBS += -L$$PWD -lmastodonbuteocommon
diff --git a/buteo-plugins/buteo-common/buteo-common.pro b/buteo-plugins/buteo-common/buteo-common.pro
deleted file mode 100644
index c0b84a9..0000000
--- a/buteo-plugins/buteo-common/buteo-common.pro
+++ /dev/null
@@ -1,33 +0,0 @@
-# SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-
-TEMPLATE = lib
-
-TARGET = mastodonbuteocommon
-TARGET = $$qtLibraryTarget($$TARGET)
-
-QT -= gui
-QT += network dbus
-CONFIG += link_pkgconfig
-PKGCONFIG += accounts-qt5 buteosyncfw5 socialcache
-
-INCLUDEPATH += $$PWD
-
-HEADERS += \
- $$PWD/buteosyncfw_p.h \
- $$PWD/socialdbuteoplugin.h \
- $$PWD/socialnetworksyncadaptor.h \
- $$PWD/socialdnetworkaccessmanager_p.h \
- $$PWD/trace.h
-
-SOURCES += \
- $$PWD/socialdbuteoplugin.cpp \
- $$PWD/socialnetworksyncadaptor.cpp \
- $$PWD/socialdnetworkaccessmanager_p.cpp \
- $$PWD/trace.cpp
-
-TARGETPATH = $$[QT_INSTALL_LIBS]
-target.path = $$TARGETPATH
-
-INSTALLS += target
diff --git a/buteo-plugins/buteo-common/buteosyncfw_p.h b/buteo-plugins/buteo-common/buteosyncfw_p.h
deleted file mode 100644
index 9fd6a77..0000000
--- a/buteo-plugins/buteo-common/buteosyncfw_p.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Chris Adams <chris.adams@jollamobile.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#ifndef SOCIALD_BUTEOSYNCFW_P_H
-#define SOCIALD_BUTEOSYNCFW_P_H
-
-#include <SyncCommonDefs.h>
-#include <SyncPluginBase.h>
-#include <ProfileManager.h>
-#include <ClientPlugin.h>
-#include <SyncResults.h>
-#include <ProfileEngineDefs.h>
-#include <SyncProfile.h>
-#include <Profile.h>
-#include <PluginCbInterface.h>
-
-#ifndef SOCIALD_TEST_DEFINE
-#define PRIVILEGED_DATA_DIR QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QLatin1String("/.local/share/system/privileged")
-#endif
-
-#endif // SOCIALD_BUTEOSYNCFW_P_H
diff --git a/buteo-plugins/buteo-common/socialdbuteoplugin.cpp b/buteo-plugins/buteo-common/socialdbuteoplugin.cpp
deleted file mode 100644
index 0eb5f91..0000000
--- a/buteo-plugins/buteo-common/socialdbuteoplugin.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Chris Adams <chris.adams@jolla.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#include "socialdbuteoplugin.h"
-#include "socialnetworksyncadaptor.h"
-#include "trace.h"
-
-#include <QCoreApplication>
-#include <QTranslator>
-
-#include <QDBusMessage>
-#include <QDBusConnection>
-#include <QDBusPendingCall>
-
-#include "buteosyncfw_p.h"
-
-#include <Accounts/Manager>
-#include <Accounts/Account>
-#include <Accounts/Service>
-
-namespace {
- const QString SyncProfileTemplatesKey = QStringLiteral("sync_profile_templates");
-
- QString SyncProfileIdKey(const QString &templateProfileName)
- {
- return QStringLiteral("%1/%2").arg(templateProfileName).arg(Buteo::KEY_PROFILE_ID);
- }
-
- QString createProfile(Buteo::ProfileManager *profileManager,
- const QString &templateProfileName,
- Accounts::Account *account,
- const Accounts::Service &srv,
- bool enableProfile,
- const QVariantMap &properties)
- {
- if (!account || !srv.isValid()) {
- qWarning() << "Invalid account or service";
- return QString();
- }
- if (templateProfileName.isEmpty()) {
- qWarning() << "Invalid templateProfileName";
- return QString();
- }
-
- Accounts::Service prevService = account->selectedService();
- account->selectService(srv);
-
- Buteo::SyncProfile *templateProfile = profileManager->syncProfile(templateProfileName);
- if (!templateProfile) {
- account->selectService(prevService);
- qWarning() << "Unable to load template profile:" << templateProfileName;
- return QString();
- }
-
- Buteo::SyncProfile *profile = templateProfile->clone();
- if (!profile) {
- delete templateProfile;
- account->selectService(prevService);
- qWarning() << "unable to clone template profile:" << templateProfileName;
- return QString();
- }
-
- QString accountIdStr = QString::number(account->id());
- profile->setName(templateProfileName + "-" + accountIdStr);
- profile->setKey(Buteo::KEY_DISPLAY_NAME, templateProfileName + "-" + account->displayName().toHtmlEscaped());
- profile->setKey(Buteo::KEY_ACCOUNT_ID, accountIdStr);
- profile->setBoolKey(Buteo::KEY_USE_ACCOUNTS, true);
- profile->setEnabled(enableProfile);
-
- // enable the profile schedule
- Buteo::SyncSchedule schedule = profile->syncSchedule();
- schedule.setScheduleEnabled(true);
- profile->setSyncSchedule(schedule);
-
- // set custom properties; note this may override any properties already set
- Q_FOREACH (const QString &key, properties.keys()) {
- profile->setKey(key, properties[key].toString());
- }
-
- QString profileId = profileManager->updateProfile(*profile);
- if (profileId.isEmpty()) {
- qWarning() << "Unable to save sync profile" << templateProfile->name();
- } else {
- account->setValue(SyncProfileIdKey(templateProfile->name()), profile->name());
- }
-
- account->selectService(prevService);
- delete profile;
- delete templateProfile;
-
- return profileId;
- }
-}
-
-SocialdButeoPlugin::SocialdButeoPlugin(const QString& pluginName,
- const Buteo::SyncProfile& profile,
- Buteo::PluginCbInterface *callbackInterface,
- const QString &socialServiceName,
- const QString &dataTypeName)
- : ClientPlugin(pluginName, profile, callbackInterface)
- , m_socialNetworkSyncAdaptor(nullptr)
- , m_socialServiceName(socialServiceName)
- , m_dataTypeName(dataTypeName)
- , m_profileAccountId(0)
-{
-}
-
-SocialdButeoPlugin::~SocialdButeoPlugin()
-{
-}
-
-bool SocialdButeoPlugin::init()
-{
- m_profileAccountId = profile().key(Buteo::KEY_ACCOUNT_ID).toInt();
- m_socialNetworkSyncAdaptor = createSocialNetworkSyncAdaptor();
- if (m_socialNetworkSyncAdaptor) {
- connect(m_socialNetworkSyncAdaptor, &SocialNetworkSyncAdaptor::statusChanged,
- this, &SocialdButeoPlugin::syncStatusChanged);
- return true;
- }
-
- return false;
-}
-
-bool SocialdButeoPlugin::uninit()
-{
- delete m_socialNetworkSyncAdaptor;
- m_socialNetworkSyncAdaptor = nullptr;
- return true;
-}
-
-bool SocialdButeoPlugin::startSync()
-{
- if (!m_socialNetworkSyncAdaptor || !m_socialNetworkSyncAdaptor->enabled()) {
- qCDebug(lcSocialPlugin) << "no enabled" << m_socialServiceName << "sync adaptor for" << m_dataTypeName;
- return false;
- }
-
- // if the profile being triggered is the template profile, then we
- // need to ensure that the appropriate per-account profiles exist.
- if (m_profileAccountId == 0) {
- QList<Buteo::SyncProfile*> perAccountProfiles = ensurePerAccountSyncProfilesExist();
- m_socialNetworkSyncAdaptor->setAccountSyncProfile(nullptr);
-
- // we need to trigger sync with each profile separately,
- // or (due to scheduling/etc) another plugin instance might
- // be created to sync that profile at the same time, and
- // we don't handle concurrency.
- foreach (Buteo::SyncProfile *perAccountProfile, perAccountProfiles) {
- QDBusMessage message = QDBusMessage::createMethodCall(
- "com.meego.msyncd", "/synchronizer", "com.meego.msyncd", "startSync");
- message.setArguments(QVariantList() << perAccountProfile->name());
- QDBusConnection::sessionBus().asyncCall(message);
- }
- qDeleteAll(perAccountProfiles);
-
- // This template profile only dispatches account-specific sync profiles.
- // Those child sync runs report their own individual results.
- updateResults(Buteo::SyncResults(QDateTime::currentDateTime(),
- Buteo::SyncResults::SYNC_RESULT_SUCCESS,
- Buteo::SyncResults::NO_ERROR));
- emit success(getProfileName(), QString("%1 update dispatched").arg(getProfileName()));
- return true;
- }
-
- Buteo::SyncProfile *accountSyncProfile = profile().clone();
- if (accountSyncProfile
- && m_dataTypeName == SocialNetworkSyncAdaptor::dataTypeName(SocialNetworkSyncAdaptor::Notifications)) {
- accountSyncProfile->setEnabled(true);
- Buteo::SyncSchedule schedule = accountSyncProfile->syncSchedule();
- schedule.setScheduleEnabled(true);
- accountSyncProfile->setSyncSchedule(schedule);
- m_profileManager.updateProfile(*accountSyncProfile);
- }
- m_socialNetworkSyncAdaptor->setAccountSyncProfile(accountSyncProfile);
-
- // Now perform sync for the account-specific profile.
- if (m_socialNetworkSyncAdaptor->status() == SocialNetworkSyncAdaptor::Inactive) {
- qCDebug(lcSocialPlugin) << "performing sync of" << m_dataTypeName << "from" << m_socialServiceName
- << "for account" << m_profileAccountId;
- m_socialNetworkSyncAdaptor->sync(m_dataTypeName, m_profileAccountId);
- return true;
- } else {
- qCDebug(lcSocialPlugin) << m_socialServiceName << "sync adaptor for" << m_dataTypeName
- << "is still busy with last sync of account" << m_profileAccountId;
- }
- return false;
-}
-
-void SocialdButeoPlugin::abortSync(Sync::SyncStatus status)
-{
- // note: it seems buteo automatically calls abortSync on network connectivity loss...
- qCInfo(lcSocialPlugin) << "aborting sync with status:" << status;
- m_socialNetworkSyncAdaptor->abortSync(status);
-}
-
-bool SocialdButeoPlugin::cleanUp()
-{
- m_profileAccountId = profile().key(Buteo::KEY_ACCOUNT_ID).toInt();
- if (!m_socialNetworkSyncAdaptor) {
- // might have already been initialized by the OOP framework via init().
- m_socialNetworkSyncAdaptor = createSocialNetworkSyncAdaptor();
- }
-
- if (m_socialNetworkSyncAdaptor && m_profileAccountId > 0) {
- m_socialNetworkSyncAdaptor->purgeDataForOldAccount(m_profileAccountId,
- SocialNetworkSyncAdaptor::CleanUpPurge);
- }
-
- return true;
-}
-
-Buteo::SyncResults SocialdButeoPlugin::getSyncResults() const
-{
- return m_syncResults;
-}
-
-void SocialdButeoPlugin::connectivityStateChanged(Sync::ConnectivityType type, bool state)
-{
- // See TransportTracker.cpp:149 for example
- // Sync::CONNECTIVITY_INTERNET, true|false
- qCInfo(lcSocialPlugin) << "notified of connectivity change:" << type << state;
- if (type == Sync::CONNECTIVITY_INTERNET && state == false) {
- // we lost connectivity during sync.
- abortSync(Sync::SYNC_CONNECTION_ERROR);
- }
-}
-
-void SocialdButeoPlugin::syncStatusChanged()
-{
- if (m_socialNetworkSyncAdaptor) {
- SocialNetworkSyncAdaptor::Status syncStatus = m_socialNetworkSyncAdaptor->status();
- // Busy change comes when sync starts -> let's ignore that.
- if (syncStatus == SocialNetworkSyncAdaptor::Inactive) {
- updateResults(Buteo::SyncResults(QDateTime::currentDateTime(),
- Buteo::SyncResults::SYNC_RESULT_SUCCESS,
- Buteo::SyncResults::NO_ERROR));
- emit success(getProfileName(), QString("%1 update succeeded").arg(getProfileName()));
- } else if (syncStatus != SocialNetworkSyncAdaptor::Busy) {
- updateResults(Buteo::SyncResults(QDateTime::currentDateTime(),
- Buteo::SyncResults::SYNC_RESULT_FAILED,
- Buteo::SyncResults::ABORTED));
- emit error(getProfileName(), QString("%1 update failed").arg(getProfileName()),
- Buteo::SyncResults::ABORTED);
- }
- } else {
- updateResults(Buteo::SyncResults(QDateTime::currentDateTime(),
- Buteo::SyncResults::SYNC_RESULT_FAILED,
- Buteo::SyncResults::ABORTED));
- emit error(getProfileName(), QString("%1 update failed").arg(getProfileName()), Buteo::SyncResults::ABORTED);
- }
-}
-
-void SocialdButeoPlugin::updateResults(const Buteo::SyncResults &results)
-{
- m_syncResults = results;
- m_syncResults.setScheduled(true);
-}
-
-// This function is called when the non-per-account profile is triggered.
-// The implementation does:
-// - get all profiles from the ProfileManager
-// - get all accounts from the AccountManager
-// - build a mapping of profile -> account for the current data type. (should be one-to-one for the datatype).
-// - any account which doesn't have a profile, print an error.
-// - check the enabled status of the account -> ensure that the enabled status is reflected in the profile.
-// It then returns a list of the appropriate (per account for this data-type) sync profiles.
-// The caller takes ownership of the list.
-QList<Buteo::SyncProfile*> SocialdButeoPlugin::ensurePerAccountSyncProfilesExist()
-{
- Accounts::Manager am;
- Accounts::AccountIdList accountIds = am.accountList();
- QList<Buteo::SyncProfile*> syncProfiles = m_profileManager.allSyncProfiles();
- QMap<Accounts::Account*, Buteo::SyncProfile*> perAccountProfiles;
-
- Accounts::Service dataTypeSyncService = am.service(m_socialNetworkSyncAdaptor->syncServiceName());
- if (!dataTypeSyncService.isValid()) {
- qWarning() << Q_FUNC_INFO << "Invalid data type sync service name specified:"
- << m_socialNetworkSyncAdaptor->syncServiceName();
- return QList<Buteo::SyncProfile*>();
- }
-
- for (int i = 0; i < accountIds.size(); ++i) {
- Accounts::Account *currAccount = Accounts::Account::fromId(&am, accountIds.at(i), this);
- if (!currAccount || currAccount->id() == 0
- || m_socialNetworkSyncAdaptor->syncServiceName().split('-').first() != currAccount->providerName()) {
- // we only generate per-account sync profiles for accounts which
- // are provided by the provider which provides our sync service.
- continue;
- }
-
- // for the current account, find the associated sync profile.
- bool foundProfile = false;
- for (int j = 0; j < syncProfiles.size(); ++j) {
- if (syncProfiles[j]->key(Buteo::KEY_ACCOUNT_ID).toInt() == QString::number(currAccount->id()).toInt()
- && syncProfiles[j]->clientProfile() != NULL
- && syncProfiles[j]->clientProfile()->name() == profile().clientProfile()->name()) {
- // we have found the sync profile for this datatype for this account.
- foundProfile = true;
- perAccountProfiles.insert(currAccount, syncProfiles.takeAt(j));
- break;
- }
- }
-
- if (!foundProfile) {
- // it should have been generated for the account when the account was added.
- qCInfo(lcSocialPlugin) << "no per-account" << profile().name()
- << "sync profile exists for account:" << currAccount->id();
-
- // create the per-account profile... we shouldn't need to do this...
- QString profileName = createProfile(&m_profileManager, profile().name(), currAccount, dataTypeSyncService, true, QVariantMap());
- Buteo::SyncProfile *newProfile = m_profileManager.syncProfile(profileName);
- if (!newProfile) {
- qCWarning(lcSocialPlugin) << "unable to create per-account" << profile().name()
- << "sync profile for account:" << currAccount->id();
- } else {
- // enable the sync schedule for the profile.
- Buteo::SyncSchedule schedule = newProfile->syncSchedule();
- schedule.setScheduleEnabled(true);
- newProfile->setSyncSchedule(schedule);
- m_profileManager.updateProfile(*newProfile);
- // and return the profile in the map.
- perAccountProfiles.insert(currAccount, newProfile);
- }
- }
- }
-
- // Every account now has the appropriate sync profile.
- qDeleteAll(syncProfiles); // these are for the wrong data type, ignore them.
- QList<Buteo::SyncProfile *> retn;
- foreach (Accounts::Account *acc, perAccountProfiles.keys()) {
- retn.append(perAccountProfiles[acc]);
- acc->deleteLater();
- }
-
- return retn;
-}
diff --git a/buteo-plugins/buteo-common/socialdbuteoplugin.h b/buteo-plugins/buteo-common/socialdbuteoplugin.h
deleted file mode 100644
index 2988d27..0000000
--- a/buteo-plugins/buteo-common/socialdbuteoplugin.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Raine Makelainen <raine.makelainen@jollamobile.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#ifndef SOCIALDBUTEOPLUGIN_H
-#define SOCIALDBUTEOPLUGIN_H
-
-#include <QtCore/qglobal.h>
-#include "buteosyncfw_p.h"
-
-/*
- Datatype-specific implementations of this class
- allow per-account sync profiles for that data type.
-*/
-
-class SocialNetworkSyncAdaptor;
-class Q_DECL_EXPORT SocialdButeoPlugin : public Buteo::ClientPlugin
-{
- Q_OBJECT
-
-protected:
- virtual SocialNetworkSyncAdaptor *createSocialNetworkSyncAdaptor() = 0;
-
-public:
- SocialdButeoPlugin(const QString& pluginName,
- const Buteo::SyncProfile& profile,
- Buteo::PluginCbInterface *cbInterface,
- const QString &socialServiceName,
- const QString &dataTypeName);
- virtual ~SocialdButeoPlugin();
-
- bool init() override;
- bool uninit() override;
- bool startSync() override;
- void abortSync(Sync::SyncStatus status = Sync::SYNC_ABORTED) override;
- Buteo::SyncResults getSyncResults() const override;
- bool cleanUp() override;
-
-public Q_SLOTS:
- void connectivityStateChanged(Sync::ConnectivityType type, bool state) override;
-
-private Q_SLOTS:
- void syncStatusChanged();
-
-protected:
- QList<Buteo::SyncProfile*> ensurePerAccountSyncProfilesExist();
-
-private:
- void updateResults(const Buteo::SyncResults &results);
- Buteo::SyncResults m_syncResults;
- Buteo::ProfileManager m_profileManager;
- SocialNetworkSyncAdaptor *m_socialNetworkSyncAdaptor;
- QString m_socialServiceName;
- QString m_dataTypeName;
- int m_profileAccountId;
-};
-
-#endif // SOCIALDBUTEOPLUGIN_H
diff --git a/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp b/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp
deleted file mode 100644
index 37b4687..0000000
--- a/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Chris Adams <chris.adams@jollamobile.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#include "socialdnetworkaccessmanager_p.h"
-
-/* The default implementation is just a normal QNetworkAccessManager */
-
-SocialdNetworkAccessManager::SocialdNetworkAccessManager(QObject *parent)
- : QNetworkAccessManager(parent)
-{
-}
-
-QNetworkReply *SocialdNetworkAccessManager::createRequest(
- QNetworkAccessManager::Operation op,
- const QNetworkRequest &req,
- QIODevice *outgoingData)
-{
- return QNetworkAccessManager::createRequest(op, req, outgoingData);
-}
diff --git a/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h b/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h
deleted file mode 100644
index 7adde92..0000000
--- a/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Chris Adams <chris.adams@jollamobile.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#ifndef SOCIALD_QNAMFACTORY_P_H
-#define SOCIALD_QNAMFACTORY_P_H
-
-#include <QNetworkAccessManager>
-
-class SocialdNetworkAccessManager : public QNetworkAccessManager
-{
- Q_OBJECT
-
-public:
- SocialdNetworkAccessManager(QObject *parent = 0);
-
-protected:
- QNetworkReply *createRequest(QNetworkAccessManager::Operation op,
- const QNetworkRequest &req,
- QIODevice *outgoingData = 0) override;
-};
-
-#endif
diff --git a/buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp b/buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp
deleted file mode 100644
index 42e6d95..0000000
--- a/buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp
+++ /dev/null
@@ -1,470 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Chris Adams <chris.adams@jollamobile.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#include "socialnetworksyncadaptor.h"
-#include "socialdnetworkaccessmanager_p.h"
-#include "trace.h"
-
-#include <QtCore/QJsonDocument>
-#include <QtCore/QTimer>
-#include <QtSql/QSqlDatabase>
-#include <QtSql/QSqlQuery>
-#include <QtSql/QSqlError>
-#include <QtSql/QSqlRecord>
-
-#include <QtNetwork/QNetworkAccessManager>
-#include <QtNetwork/QNetworkReply>
-
-#include "buteosyncfw_p.h"
-
-// libaccounts-qt5
-#include <Accounts/Manager>
-#include <Accounts/Account>
-#include <Accounts/Service>
-
-// libsocialcache
-#include <socialcache/socialimagesdatabase.h>
-#include <socialcache/socialnetworksyncdatabase.h>
-
-namespace {
- QStringList validDataTypesInitialiser()
- {
- return QStringList()
- << QStringLiteral("Contacts")
- << QStringLiteral("Calendars")
- << QStringLiteral("Notifications")
- << QStringLiteral("Images")
- << QStringLiteral("Videos")
- << QStringLiteral("Posts")
- << QStringLiteral("Messages")
- << QStringLiteral("Emails")
- << QStringLiteral("Signon")
- << QStringLiteral("Backup")
- << QStringLiteral("BackupQuery")
- << QStringLiteral("BackupRestore");
- }
-}
-
-SocialNetworkSyncAdaptor::SocialNetworkSyncAdaptor(const QString &serviceName,
- SocialNetworkSyncAdaptor::DataType dataType,
- QNetworkAccessManager *qnam,
- QObject *parent)
- : QObject(parent)
- , m_dataType(dataType)
- , m_accountManager(new Accounts::Manager(this))
- , m_networkAccessManager(qnam != 0 ? qnam : new SocialdNetworkAccessManager)
- , m_accountSyncProfile(NULL)
- , m_syncDb(new SocialNetworkSyncDatabase())
- , m_status(SocialNetworkSyncAdaptor::Invalid)
- , m_enabled(false)
- , m_syncAborted(false)
- , m_serviceName(serviceName)
-{
-}
-
-SocialNetworkSyncAdaptor::~SocialNetworkSyncAdaptor()
-{
- delete m_networkAccessManager;
- delete m_accountSyncProfile;
- delete m_syncDb;
-}
-
-// The SocialNetworkSyncAdaptor takes ownership of the sync profiles.
-void SocialNetworkSyncAdaptor::setAccountSyncProfile(Buteo::SyncProfile* perAccountSyncProfile)
-{
- delete m_accountSyncProfile;
- m_accountSyncProfile = perAccountSyncProfile;
-}
-
-SocialNetworkSyncAdaptor::Status SocialNetworkSyncAdaptor::status() const
-{
- return m_status;
-}
-
-bool SocialNetworkSyncAdaptor::enabled() const
-{
- return m_enabled;
-}
-
-QString SocialNetworkSyncAdaptor::serviceName() const
-{
- return m_serviceName;
-}
-
-bool SocialNetworkSyncAdaptor::syncAborted() const
-{
- return m_syncAborted;
-}
-
-void SocialNetworkSyncAdaptor::sync(const QString &dataType, int accountId)
-{
- Q_UNUSED(dataType)
- Q_UNUSED(accountId)
- qCWarning(lcSocialPlugin) << "sync() must be overridden by derived types";
-}
-
-void SocialNetworkSyncAdaptor::abortSync(Sync::SyncStatus status)
-{
- qCInfo(lcSocialPlugin) << "forcing timeout of outstanding replies due to abort:" << status;
- m_syncAborted = true;
- triggerReplyTimeouts();
-}
-
-/*!
- * \brief SocialNetworkSyncAdaptor::checkAccount
- * \param account
- * \return true if synchronization of this adaptor's datatype is enabled for the account
- *
- * The default implementation checks that the account is enabled
- * with the accounts&sso service associated with this sync adaptor.
- */
-bool SocialNetworkSyncAdaptor::checkAccount(Accounts::Account *account)
-{
- bool globallyEnabled = account->enabled();
- Accounts::Service srv(m_accountManager->service(syncServiceName()));
- if (!srv.isValid()) {
- qCInfo(lcSocialPlugin) << "invalid service" << syncServiceName() << "specified, account" << account->id()
- << "will be disabled for" << m_serviceName << dataTypeName(m_dataType) << "sync";
- return false;
- }
- account->selectService(srv);
- bool serviceEnabled = account->enabled();
- account->selectService(Accounts::Service());
- return globallyEnabled && serviceEnabled;
-}
-
-/*!
- \internal
- Called when the semaphores for all accounts have been decreased
- to zero. This is the final function which is called prior to
- telling buteo that the sync plugin can be destroyed.
- The implementation MUST be synchronous.
-*/
-void SocialNetworkSyncAdaptor::finalCleanup()
-{
-}
-
-/*!
- \internal
- Called when the semaphores decreased to 0, this method is used
- to finalize something, like saving all data to a database.
-
- You can call incrementSemaphore to perform asynchronous tasks
- in this method. finalize will then be called again when the
- asynchronous task is finished (and when decrementSemaphore is
- called), be sure to have a condition check in order not to run
- into an infinite loop.
-
- It is unsafe to call decrementSemaphore in this method, as
- the semaphore handling method will find that the semaphore
- went to 0 twice and will perform cleanup operations twice.
- Please call decrementSemaphore at the end of the asynchronous
- task (preferably in a slot), and only call incrementSemaphore
- for asynchronous tasks.
- */
-void SocialNetworkSyncAdaptor::finalize(int accountId)
-{
- Q_UNUSED(accountId)
-}
-
-/*!
- \internal
- Returns the last sync timestamp for the given service, account and data type.
- If data from prior to this timestamp is received in subsequent requests, it does not need to be synced.
- This function will return an invalid QDateTime if no synchronisation has occurred.
-*/
-QDateTime SocialNetworkSyncAdaptor::lastSyncTimestamp(const QString &serviceName,
- const QString &dataType,
- int accountId) const
-{
- return m_syncDb->lastSyncTimestamp(serviceName, dataType, accountId);
-}
-
-/*!
- \internal
- Updates the last sync timestamp for the given service, account and data type to the given \a timestamp.
-*/
-bool SocialNetworkSyncAdaptor::updateLastSyncTimestamp(const QString &serviceName,
- const QString &dataType,
- int accountId,
- const QDateTime &timestamp)
-{
- // Workaround
- // TODO: do better, with a queue
- m_syncDb->addSyncTimestamp(serviceName, dataType, accountId, timestamp);
- m_syncDb->commit();
- m_syncDb->wait();
- return m_syncDb->writeStatus() == AbstractSocialCacheDatabase::Finished;
-}
-
-/*!
- \internal
- Returns the list of identifiers of accounts which have been synced for
- the given \a dataType.
-*/
-QList<int> SocialNetworkSyncAdaptor::syncedAccounts(const QString &dataType)
-{
- return m_syncDb->syncedAccounts(m_serviceName, dataType);
-}
-
-/*!
- * \internal
- * Changes status if there is real change and emits statusChanged() signal.
- */
-void SocialNetworkSyncAdaptor::setStatus(Status status)
-{
- if (m_status != status) {
- m_status = status;
- emit statusChanged();
- }
-}
-
-/*!
- * \internal
- * Should be used in constructors to set the initial state
- * of enabled and status, without emitting signals
- *
- */
-void SocialNetworkSyncAdaptor::setInitialActive(bool enabled)
-{
- m_enabled = enabled;
- if (enabled) {
- m_status = Inactive;
- } else {
- m_status = Invalid;
- }
-}
-
-/*!
- * \internal
- * Should be called by any specific sync adapter when
- * they've finished syncing data. The transition from
- * busy status to inactive status is what causes the
- * Buteo plugin to emit the sync results (and allows
- * subsequent syncs to occur).
- */
-void SocialNetworkSyncAdaptor::setFinishedInactive()
-{
- finalCleanup();
- qCInfo(lcSocialPlugin) << "Finished" << m_serviceName << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
- << "sync at:" << QDateTime::currentDateTime().toString(Qt::ISODate);
- setStatus(SocialNetworkSyncAdaptor::Inactive);
-}
-
-void SocialNetworkSyncAdaptor::incrementSemaphore(int accountId)
-{
- int semaphoreValue = m_accountSyncSemaphores.value(accountId);
- semaphoreValue += 1;
- m_accountSyncSemaphores.insert(accountId, semaphoreValue);
- qCDebug(lcSocialPlugin) << "incremented busy semaphore for account" << accountId << "to:" << semaphoreValue;
-}
-
-void SocialNetworkSyncAdaptor::decrementSemaphore(int accountId)
-{
- if (!m_accountSyncSemaphores.contains(accountId)) {
- qCWarning(lcSocialPlugin) << "no such semaphore for account" << accountId;
- return;
- }
-
- int semaphoreValue = m_accountSyncSemaphores.value(accountId);
- semaphoreValue -= 1;
- qCDebug(lcSocialPlugin) << "decremented busy semaphore for account" << accountId << "to:" << semaphoreValue;
- if (semaphoreValue < 0) {
- qCWarning(lcSocialPlugin) << "busy semaphore is negative for account" << accountId;
- return;
- }
- m_accountSyncSemaphores.insert(accountId, semaphoreValue);
-
- if (semaphoreValue == 0) {
- finalize(accountId);
-
- // With the newer implementation, in finalize we can raise semaphores,
- // so if after calling finalize, the semaphore count is not the same anymore,
- // we shouldn't update the sync timestamp
- if (m_accountSyncSemaphores.value(accountId) > 0) {
- return;
- }
-
- // finished all outstanding sync requests for this account.
- // update the sync time in the global sociald database.
- updateLastSyncTimestamp(m_serviceName,
- SocialNetworkSyncAdaptor::dataTypeName(m_dataType), accountId,
- QDateTime::currentDateTime().toTimeSpec(Qt::UTC));
-
- // if all outstanding requests for all accounts have finished,
- // then update our status to Inactive / ready to handle more sync requests.
- bool allAreZero = true;
- QList<int> semaphores = m_accountSyncSemaphores.values();
- foreach (int sv, semaphores) {
- if (sv != 0) {
- allAreZero = false;
- break;
- }
- }
-
- if (allAreZero) {
- setFinishedInactive(); // Finished!
- }
- }
-}
-
-void SocialNetworkSyncAdaptor::timeoutReply()
-{
- QTimer *timer = qobject_cast<QTimer*>(sender());
- QNetworkReply *reply = timer->property("networkReply").value<QNetworkReply*>();
- int accountId = timer->property("accountId").toInt();
-
- qCWarning(lcSocialPlugin) << "network request timed out while performing sync with account" << accountId;
-
- m_networkReplyTimeouts[accountId].remove(reply);
- reply->setProperty("isError", QVariant::fromValue<bool>(true));
- reply->finished(); // invoke finished, so that the error handling there decrements the semaphore etc.
- reply->disconnect();
-}
-
-void SocialNetworkSyncAdaptor::setupReplyTimeout(int accountId, QNetworkReply *reply, int msecs)
-{
- // this function should be called whenever a new network request is performed.
- QTimer *timer = new QTimer(this);
- timer->setSingleShot(true);
- timer->setInterval(msecs);
- timer->setProperty("accountId", accountId);
- timer->setProperty("networkReply", QVariant::fromValue<QNetworkReply*>(reply));
- connect(timer, &QTimer::timeout, this, &SocialNetworkSyncAdaptor::timeoutReply);
- timer->start();
- m_networkReplyTimeouts[accountId].insert(reply, timer);
-}
-
-void SocialNetworkSyncAdaptor::removeReplyTimeout(int accountId, QNetworkReply *reply)
-{
- // this function should be called by the finished() handler for the reply.
- QTimer *timer = m_networkReplyTimeouts[accountId].value(reply);
- if (!reply) {
- return;
- }
-
- delete timer;
- m_networkReplyTimeouts[accountId].remove(reply);
-}
-
-void SocialNetworkSyncAdaptor::triggerReplyTimeouts()
-{
- // if we've lost network connectivity, we should immediately timeout all replies.
- Q_FOREACH (int accountId, m_networkReplyTimeouts.keys()) {
- Q_FOREACH (QTimer *timer, m_networkReplyTimeouts[accountId]) {
- timer->stop();
- timer->setInterval(1);
- timer->start();
- }
- }
-}
-
-QJsonObject SocialNetworkSyncAdaptor::parseJsonObjectReplyData(const QByteArray &replyData, bool *ok)
-{
- QJsonDocument jsonDocument = QJsonDocument::fromJson(replyData);
- *ok = !jsonDocument.isEmpty();
- if (*ok && jsonDocument.isObject()) {
- return jsonDocument.object();
- }
- *ok = false;
- return QJsonObject();
-}
-
-QJsonArray SocialNetworkSyncAdaptor::parseJsonArrayReplyData(const QByteArray &replyData, bool *ok)
-{
- QJsonDocument jsonDocument = QJsonDocument::fromJson(replyData);
- *ok = !jsonDocument.isEmpty();
- if (*ok && jsonDocument.isArray()) {
- return jsonDocument.array();
- }
- *ok = false;
- return QJsonArray();
-}
-
-/*
- Valid data types are data types which are known to the API.
- Note that just because a data type is valid does not mean
- that it will necessarily be supported by a given social network
- sync adaptor.
-*/
-QStringList SocialNetworkSyncAdaptor::validDataTypes()
-{
- static QStringList retn(validDataTypesInitialiser());
- return retn;
-}
-
-/*
- String for Enum since the DBus API uses strings
-*/
-QString SocialNetworkSyncAdaptor::dataTypeName(SocialNetworkSyncAdaptor::DataType t)
-{
- switch (t) {
- case SocialNetworkSyncAdaptor::Contacts: return QStringLiteral("Contacts");
- case SocialNetworkSyncAdaptor::Calendars: return QStringLiteral("Calendars");
- case SocialNetworkSyncAdaptor::Notifications: return QStringLiteral("Notifications");
- case SocialNetworkSyncAdaptor::Images: return QStringLiteral("Images");
- case SocialNetworkSyncAdaptor::Videos: return QStringLiteral("Videos");
- case SocialNetworkSyncAdaptor::Posts: return QStringLiteral("Posts");
- case SocialNetworkSyncAdaptor::Messages: return QStringLiteral("Messages");
- case SocialNetworkSyncAdaptor::Emails: return QStringLiteral("Emails");
- case SocialNetworkSyncAdaptor::Signon: return QStringLiteral("Signon");
- case SocialNetworkSyncAdaptor::Backup: return QStringLiteral("Backup");
- case SocialNetworkSyncAdaptor::BackupQuery: return QStringLiteral("BackupQuery");
- case SocialNetworkSyncAdaptor::BackupRestore: return QStringLiteral("BackupRestore");
- default: break;
- }
-
- return QString();
-}
-
-void SocialNetworkSyncAdaptor::purgeCachedImages(SocialImagesDatabase *database,
- int accountId)
-{
- database->queryImages(accountId);
- database->wait();
-
- QList<SocialImage::ConstPtr> images = database->images();
- foreach (SocialImage::ConstPtr image, images) {
- qCDebug(lcSocialPlugin) << "Purge cached image " << image->imageFile() << " for account " << image->accountId();
- QFile::remove(image->imageFile());
- }
-
- database->removeImages(images);
- database->commit();
- database->wait();
-}
-
-void SocialNetworkSyncAdaptor::purgeExpiredImages(SocialImagesDatabase *database,
- int accountId)
-{
- database->queryExpired(accountId);
- database->wait();
-
- QList<SocialImage::ConstPtr> images = database->images();
- foreach (SocialImage::ConstPtr image, images) {
- qCDebug(lcSocialPlugin) << "Purge expired image " << image->imageFile() << " for account " << image->accountId();
- QFile::remove(image->imageFile());
- }
-
- database->removeImages(images);
- database->commit();
- database->wait();
-}
diff --git a/buteo-plugins/buteo-common/socialnetworksyncadaptor.h b/buteo-plugins/buteo-common/socialnetworksyncadaptor.h
deleted file mode 100644
index 7c76564..0000000
--- a/buteo-plugins/buteo-common/socialnetworksyncadaptor.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Chris Adams <chris.adams@jollamobile.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#ifndef SOCIALNETWORKSYNCADAPTOR_H
-#define SOCIALNETWORKSYNCADAPTOR_H
-
-#include <QtCore/QObject>
-#include <QtCore/QDateTime>
-#include <QtCore/QString>
-#include <QtCore/QJsonObject>
-#include <QtCore/QJsonArray>
-#include <QtCore/QMap>
-#include <QtCore/QList>
-
-#include "buteosyncfw_p.h"
-
-class QSqlDatabase;
-class QNetworkAccessManager;
-class QTimer;
-class QNetworkReply;
-class SocialNetworkSyncDatabase;
-class SocialImagesDatabase;
-
-namespace Accounts {
- class Account;
- class Manager;
-}
-
-class SocialNetworkSyncAdaptor : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(Status status READ status NOTIFY statusChanged)
- Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
-
-public:
- enum Status {
- Initializing = 0,
- Inactive,
- Busy,
- Error,
- Invalid
- };
-
- enum PurgeMode {
- SyncPurge = 0,
- CleanUpPurge
- };
-
- enum DataType {
- Contacts = 1, // "Contacts"
- Calendars, // "Calendars"
- Notifications, // "Notifications"
- Images, // "Images"
- Videos, // "Videos"
- Posts, // "Posts"
- Messages, // "Messages"
- Emails, // "Emails"
- Signon, // "Signon" -- for refreshing AccessTokens etc.
- Backup, // "Backup"
- BackupQuery, // "BackupQuery"
- BackupRestore // "BackupRestore"
- };
- static QStringList validDataTypes();
- static QString dataTypeName(DataType t);
-
-public:
- SocialNetworkSyncAdaptor(const QString &serviceName, SocialNetworkSyncAdaptor::DataType dataType,
- QNetworkAccessManager *qnam, QObject *parent);
- virtual ~SocialNetworkSyncAdaptor();
-
- virtual QString syncServiceName() const = 0;
- void setAccountSyncProfile(Buteo::SyncProfile* perAccountSyncProfile);
-
- Status status() const;
- bool enabled() const;
- QString serviceName() const;
-
- virtual void sync(const QString &dataType, int accountId = 0);
- virtual void purgeDataForOldAccount(int accountId, PurgeMode mode = SyncPurge) = 0;
- virtual void abortSync(Sync::SyncStatus status);
-
-Q_SIGNALS:
- void statusChanged();
- void enabledChanged();
-
-protected:
- virtual bool checkAccount(Accounts::Account *account);
- virtual void finalCleanup();
- virtual void finalize(int accountId);
- QDateTime lastSyncTimestamp(const QString &serviceName, const QString &dataType,
- int accountId) const;
- bool updateLastSyncTimestamp(const QString &serviceName, const QString &dataType,
- int accountId, const QDateTime &timestamp);
- QList<int> syncedAccounts(const QString &dataType);
- void setStatus(Status status);
- void setInitialActive(bool enabled);
- void setFinishedInactive();
-
- // whether the sync has been aborted (perhaps due to network connection loss)
- bool syncAborted() const;
-
- // Semaphore system
- void incrementSemaphore(int accountId);
- void decrementSemaphore(int accountId);
-
- // network reply timeouts
- void setupReplyTimeout(int accountId, QNetworkReply *reply, int msecs = 60000);
- void removeReplyTimeout(int accountId, QNetworkReply *reply);
- void triggerReplyTimeouts();
-
- // Parsing methods
- static QJsonObject parseJsonObjectReplyData(const QByteArray &replyData, bool *ok);
- static QJsonArray parseJsonArrayReplyData(const QByteArray &replyData, bool *ok);
-
- // Cache management
- void purgeCachedImages(SocialImagesDatabase *database, int accountId);
- void purgeExpiredImages(SocialImagesDatabase *database, int accountId);
-
- const SocialNetworkSyncAdaptor::DataType m_dataType;
- Accounts::Manager * const m_accountManager;
- QNetworkAccessManager * const m_networkAccessManager;
- Buteo::SyncProfile *m_accountSyncProfile;
-
-protected Q_SLOTS:
- virtual void timeoutReply();
-
-private:
- SocialNetworkSyncDatabase *m_syncDb;
- SocialNetworkSyncAdaptor::Status m_status;
- bool m_enabled;
- bool m_syncAborted;
- QString m_serviceName;
- QMap<int, int> m_accountSyncSemaphores;
- QMap<int, QMap<QNetworkReply*, QTimer *> > m_networkReplyTimeouts;
-};
-
-#endif // SOCIALNETWORKSYNCADAPTOR_H
diff --git a/buteo-plugins/buteo-common/trace.cpp b/buteo-plugins/buteo-common/trace.cpp
deleted file mode 100644
index 05e3508..0000000
--- a/buteo-plugins/buteo-common/trace.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#include "trace.h"
-
-Q_LOGGING_CATEGORY(lcSocialPlugin, "buteo.plugin.social", QtWarningMsg)
-Q_LOGGING_CATEGORY(lcSocialPluginTrace, "buteo.plugin.social.trace", QtWarningMsg)
-
diff --git a/buteo-plugins/buteo-common/trace.h b/buteo-plugins/buteo-common/trace.h
deleted file mode 100644
index 2b9c1ae..0000000
--- a/buteo-plugins/buteo-common/trace.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/****************************************************************************
- **
- ** Copyright (C) 2013-2026 Jolla Ltd.
- ** Contact: Chris Adams <chris.adams@jollamobile.com>
- **
- ** This program/library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Lesser General Public License
- ** version 2.1 as published by the Free Software Foundation.
- **
- ** This program/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 program/library; if not, write to the Free
- ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- ** 02110-1301 USA
- **
- ****************************************************************************/
-
-#ifndef TRACE_H
-#define TRACE_H
-
-#include <QLoggingCategory>
-
-Q_DECLARE_LOGGING_CATEGORY(lcSocialPlugin)
-Q_DECLARE_LOGGING_CATEGORY(lcSocialPluginTrace)
-
-#endif // TRACE_H
diff --git a/buteo-plugins/buteo-plugins.pro b/buteo-plugins/buteo-plugins.pro
index ead60e6..a64041b 100644
--- a/buteo-plugins/buteo-plugins.pro
+++ b/buteo-plugins/buteo-plugins.pro
@@ -4,9 +4,5 @@
TEMPLATE = subdirs
SUBDIRS += \
- buteo-common \
buteo-sync-plugin-mastodon-posts \
buteo-sync-plugin-mastodon-notifications
-
-buteo-sync-plugin-mastodon-posts.depends = buteo-common
-buteo-sync-plugin-mastodon-notifications.depends = buteo-common
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/buteo-sync-plugin-mastodon-notifications.pro b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/buteo-sync-plugin-mastodon-notifications.pro
index d16cc3d..dda88ff 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/buteo-sync-plugin-mastodon-notifications.pro
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/buteo-sync-plugin-mastodon-notifications.pro
@@ -6,7 +6,6 @@ TARGET = mastodon-notifications-client
QT -= gui
-include($$PWD/../buteo-common/buteo-common.pri)
include($$PWD/../../common/common.pri)
TS_FILE = $$OUT_PWD/lipstick-jolla-home-mastodon-notifications.ts
@@ -35,7 +34,14 @@ QMAKE_EXTRA_TARGETS += ts engineering_english
PRE_TARGETDEPS += ts engineering_english
CONFIG += link_pkgconfig
-PKGCONFIG += mlite5 nemonotifications-qt5
+PKGCONFIG += \
+ buteosocialcommon \
+ socialcache \
+ accounts-qt5 \
+ buteosyncfw5 \
+ libsignon-qt5 \
+ mlite5 \
+ nemonotifications-qt5
INCLUDEPATH += $$PWD
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-posts/buteo-sync-plugin-mastodon-posts.pro b/buteo-plugins/buteo-sync-plugin-mastodon-posts/buteo-sync-plugin-mastodon-posts.pro
index a9f65af..c25e5d4 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-posts/buteo-sync-plugin-mastodon-posts.pro
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-posts/buteo-sync-plugin-mastodon-posts.pro
@@ -6,11 +6,17 @@ TARGET = mastodon-posts-client
QT -= gui
-include($$PWD/../buteo-common/buteo-common.pri)
include($$PWD/../../common/common.pri)
CONFIG += link_pkgconfig
-PKGCONFIG += mlite5 nemonotifications-qt5
+PKGCONFIG += \
+ buteosocialcommon \
+ socialcache \
+ accounts-qt5 \
+ buteosyncfw5 \
+ libsignon-qt5 \
+ mlite5 \
+ nemonotifications-qt5
INCLUDEPATH += $$PWD
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp
index 7b47fe8..577e185 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodondatatypesyncadaptor.cpp
@@ -20,8 +20,8 @@
#include "mastodondatatypesyncadaptor.h"
#include "mastodonauthutils.h"
-#include "trace.h"
+#include <QtCore/QLoggingCategory>
#include <QtCore/QVariantMap>
#include <QtNetwork/QNetworkRequest>
@@ -36,6 +36,8 @@
#include <SignOn/AuthSession>
#include <SignOn/SessionData>
+Q_LOGGING_CATEGORY(lcMastodonSync, "buteo.plugin.mastodon.sync", QtWarningMsg)
+
MastodonDataTypeSyncAdaptor::MastodonDataTypeSyncAdaptor(
SocialNetworkSyncAdaptor::DataType dataType,
QObject *parent)
@@ -50,7 +52,7 @@ MastodonDataTypeSyncAdaptor::~MastodonDataTypeSyncAdaptor()
void MastodonDataTypeSyncAdaptor::sync(const QString &dataTypeString, int accountId)
{
if (dataTypeString != SocialNetworkSyncAdaptor::dataTypeName(m_dataType)) {
- qCWarning(lcSocialPlugin) << "Mastodon" << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
+ qCWarning(lcMastodonSync) << "Mastodon" << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
<< "sync adaptor was asked to sync" << dataTypeString;
setStatus(SocialNetworkSyncAdaptor::Error);
return;
@@ -58,14 +60,14 @@ void MastodonDataTypeSyncAdaptor::sync(const QString &dataTypeString, int accoun
setStatus(SocialNetworkSyncAdaptor::Busy);
updateDataForAccount(accountId);
- qCDebug(lcSocialPlugin) << "successfully triggered sync with profile:" << m_accountSyncProfile->name();
+ qCDebug(lcMastodonSync) << "successfully triggered sync with profile:" << m_accountSyncProfile->name();
}
void MastodonDataTypeSyncAdaptor::updateDataForAccount(int accountId)
{
Accounts::Account *account = Accounts::Account::fromId(m_accountManager, accountId, this);
if (!account) {
- qCWarning(lcSocialPlugin) << "existing account with id" << accountId << "couldn't be retrieved";
+ qCWarning(lcMastodonSync) << "existing account with id" << accountId << "couldn't be retrieved";
setStatus(SocialNetworkSyncAdaptor::Error);
return;
}
@@ -89,7 +91,7 @@ void MastodonDataTypeSyncAdaptor::errorHandler(QNetworkReply::NetworkError err)
const int accountId = reply->property("accountId").toInt();
const int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- qCWarning(lcSocialPlugin) << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
+ qCWarning(lcMastodonSync) << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
<< "request with account" << accountId
<< "experienced error:" << err
<< "HTTP:" << httpStatus;
@@ -114,7 +116,7 @@ void MastodonDataTypeSyncAdaptor::sslErrorsHandler(const QList<QSslError> &errs)
sslerrs.chop(2);
}
- qCWarning(lcSocialPlugin) << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
+ qCWarning(lcMastodonSync) << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
<< "request with account" << sender()->property("accountId").toInt()
<< "experienced ssl errors:" << sslerrs;
sender()->setProperty("isError", QVariant::fromValue<bool>(true));
@@ -122,7 +124,7 @@ void MastodonDataTypeSyncAdaptor::sslErrorsHandler(const QList<QSslError> &errs)
void MastodonDataTypeSyncAdaptor::setCredentialsNeedUpdate(Accounts::Account *account)
{
- qCInfo(lcSocialPlugin) << "sociald:Mastodon: setting CredentialsNeedUpdate to true for account:" << account->id();
+ qCInfo(lcMastodonSync) << "sociald:Mastodon: setting CredentialsNeedUpdate to true for account:" << account->id();
Accounts::Service srv(m_accountManager->service(syncServiceName()));
account->selectService(srv);
account->setValue(QStringLiteral("CredentialsNeedUpdate"), QVariant::fromValue<bool>(true));
@@ -145,7 +147,7 @@ void MastodonDataTypeSyncAdaptor::signIn(Accounts::Account *account)
? SignOn::Identity::existingIdentity(account->credentialsId())
: 0;
if (!identity) {
- qCWarning(lcSocialPlugin) << "account" << accountId << "has no valid credentials, cannot sign in";
+ qCWarning(lcMastodonSync) << "account" << accountId << "has no valid credentials, cannot sign in";
decrementSemaphore(accountId);
return;
}
@@ -155,7 +157,7 @@ void MastodonDataTypeSyncAdaptor::signIn(Accounts::Account *account)
const QString mechanism = accSrv.authData().mechanism();
SignOn::AuthSession *session = identity->createSession(method);
if (!session) {
- qCWarning(lcSocialPlugin) << "could not create signon session for account" << accountId;
+ qCWarning(lcMastodonSync) << "could not create signon session for account" << accountId;
identity->deleteLater();
decrementSemaphore(accountId);
return;
@@ -183,7 +185,7 @@ void MastodonDataTypeSyncAdaptor::signOnError(const SignOn::Error &error)
SignOn::Identity *identity = session->property("identity").value<SignOn::Identity*>();
const int accountId = account->id();
- qCWarning(lcSocialPlugin) << "credentials for account with id" << accountId
+ qCWarning(lcMastodonSync) << "credentials for account with id" << accountId
<< "couldn't be retrieved:" << error.type() << error.message();
if (error.type() == SignOn::Error::UserInteraction) {
@@ -211,7 +213,7 @@ void MastodonDataTypeSyncAdaptor::signOnResponse(const SignOn::SessionData &resp
accessToken = MastodonAuthUtils::accessToken(data);
if (accessToken.isEmpty()) {
- qCWarning(lcSocialPlugin) << "signon response for account with id" << accountId
+ qCWarning(lcMastodonSync) << "signon response for account with id" << accountId
<< "contained no access token; keys:" << data.keys();
}
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodonpostssyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodonpostssyncadaptor.cpp
index 160d6cc..6c9a280 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodonpostssyncadaptor.cpp
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-posts/mastodonpostssyncadaptor.cpp
@@ -19,9 +19,9 @@
****************************************************************************/
#include "mastodonpostssyncadaptor.h"
-#include "trace.h"
#include "mastodontextutils.h"
+#include <QtCore/QLoggingCategory>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
@@ -30,6 +30,8 @@
#include <QtNetwork/QNetworkRequest>
namespace {
+ Q_LOGGING_CATEGORY(lcMastodonPostsSync, "buteo.plugin.mastodon.posts.sync", QtWarningMsg)
+
QString displayNameForAccount(const QJsonObject &account)
{
const QString displayName = account.value(QStringLiteral("display_name")).toString().trimmed();
@@ -80,7 +82,7 @@ void MastodonPostsSyncAdaptor::beginSync(int accountId, const QString &accessTok
void MastodonPostsSyncAdaptor::finalize(int accountId)
{
if (syncAborted()) {
- qCInfo(lcSocialPlugin) << "sync aborted, won't commit database changes";
+ qCInfo(lcMastodonPostsSync) << "sync aborted, won't commit database changes";
} else {
m_db.commit();
m_db.wait();
@@ -121,7 +123,7 @@ void MastodonPostsSyncAdaptor::requestPosts(int accountId, const QString &access
incrementSemaphore(accountId);
setupReplyTimeout(accountId, reply);
} else {
- qCWarning(lcSocialPlugin) << "unable to request home timeline posts from Mastodon account with id" << accountId;
+ qCWarning(lcMastodonPostsSync) << "unable to request home timeline posts from Mastodon account with id" << accountId;
}
}
@@ -146,7 +148,7 @@ void MastodonPostsSyncAdaptor::finishedPostsHandler()
m_db.removePosts(accountId);
if (!statuses.size()) {
- qCDebug(lcSocialPlugin) << "no feed posts received for account" << accountId;
+ qCDebug(lcMastodonPostsSync) << "no feed posts received for account" << accountId;
decrementSemaphore(accountId);
return;
}
@@ -249,7 +251,7 @@ void MastodonPostsSyncAdaptor::finishedPostsHandler()
accountId);
}
} else {
- qCWarning(lcSocialPlugin) << "unable to parse event feed data from request with account" << accountId
+ qCWarning(lcMastodonPostsSync) << "unable to parse event feed data from request with account" << accountId
<< ", got:" << QString::fromUtf8(replyData);
}
diff --git a/common/mastodonpostsdatabase.cpp b/common/mastodonpostsdatabase.cpp
index 7f82162..2e4b9c1 100644
--- a/common/mastodonpostsdatabase.cpp
+++ b/common/mastodonpostsdatabase.cpp
@@ -17,6 +17,7 @@
*/
#include "mastodonpostsdatabase.h"
+#include <socialcache/socialposthelpers.h>
static const char *DB_NAME = "mastodon.db";
static const char *ACCOUNT_NAME_KEY = "account_name";
@@ -71,72 +72,45 @@ void MastodonPostsDatabase::addMastodonPost(
QString MastodonPostsDatabase::accountName(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return QString();
- }
- return post->extra().value(ACCOUNT_NAME_KEY).toString();
+ return SocialPostHelpers::extraString(post, QString::fromLatin1(ACCOUNT_NAME_KEY));
}
QString MastodonPostsDatabase::url(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return QString();
- }
- return post->extra().value(URL_KEY).toString();
+ return SocialPostHelpers::extraString(post, QString::fromLatin1(URL_KEY));
}
QString MastodonPostsDatabase::boostedBy(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return QString();
- }
- return post->extra().value(BOOSTED_BY_KEY).toString();
+ return SocialPostHelpers::extraString(post, QString::fromLatin1(BOOSTED_BY_KEY));
}
int MastodonPostsDatabase::repliesCount(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return 0;
- }
- return post->extra().value(REPLIES_COUNT_KEY).toInt();
+ return SocialPostHelpers::extraInt(post, QString::fromLatin1(REPLIES_COUNT_KEY), 0);
}
int MastodonPostsDatabase::favouritesCount(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return 0;
- }
- return post->extra().value(FAVOURITES_COUNT_KEY).toInt();
+ return SocialPostHelpers::extraInt(post, QString::fromLatin1(FAVOURITES_COUNT_KEY), 0);
}
int MastodonPostsDatabase::reblogsCount(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return 0;
- }
- return post->extra().value(REBLOGS_COUNT_KEY).toInt();
+ return SocialPostHelpers::extraInt(post, QString::fromLatin1(REBLOGS_COUNT_KEY), 0);
}
bool MastodonPostsDatabase::favourited(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return false;
- }
- return post->extra().value(FAVOURITED_KEY).toBool();
+ return SocialPostHelpers::extraBool(post, QString::fromLatin1(FAVOURITED_KEY), false);
}
bool MastodonPostsDatabase::reblogged(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return false;
- }
- return post->extra().value(REBLOGGED_KEY).toBool();
+ return SocialPostHelpers::extraBool(post, QString::fromLatin1(REBLOGGED_KEY), false);
}
QString MastodonPostsDatabase::instanceUrl(const SocialPost::ConstPtr &post)
{
- if (post.isNull()) {
- return QString();
- }
- return post->extra().value(INSTANCE_URL_KEY).toString();
+ return SocialPostHelpers::extraString(post, QString::fromLatin1(INSTANCE_URL_KEY));
}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml b/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml
index 63b9556..e9c76da 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml
+++ b/eventsview-plugins/eventsview-plugin-mastodon/MastodonFeedItem.qml
@@ -15,14 +15,14 @@ SocialMediaFeedItem {
id: item
property variant imageList
- property string resolvedStatusUrl: item.stringValue("url", "link", "uri")
+ property string resolvedStatusUrl: model && model.url ? model.url.toString() : ""
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 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
@@ -41,6 +41,18 @@ SocialMediaFeedItem {
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.
+ //% "Favourite"
+ readonly property string _favouriteActionText: qsTrId("lipstick-jolla-home-la-mastodon_favourite")
+ //: Action label shown in Mastodon interaction menu when the post is already favourited.
+ //% "Unfavourite"
+ readonly property string _unfavouriteActionText: qsTrId("lipstick-jolla-home-la-mastodon_unfavourite")
+ //: Action label shown in Mastodon interaction menu.
+ //% "Boost"
+ readonly property string _boostActionText: qsTrId("lipstick-jolla-home-la-mastodon_boost")
+ //: Action label shown in Mastodon interaction menu when the post is already boosted.
+ //% "Undo boost"
+ readonly property string _unboostActionText: qsTrId("lipstick-jolla-home-la-mastodon_unboost")
+ //: 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.
@@ -86,7 +98,7 @@ SocialMediaFeedItem {
topMargin: item._booster.length > 0 ? Theme.paddingMedium : Theme.paddingLarge
userRemovable: false
- Image {
+ SocialReshareIcon {
id: boosterIcon
anchors {
@@ -95,21 +107,17 @@ SocialMediaFeedItem {
topMargin: item.topMargin
}
visible: item._booster.length > 0
- source: "image://theme/icon-s-repost" + (item.highlighted ? "?" + Theme.highlightColor : "")
+ highlighted: item.highlighted
+ iconSource: "image://theme/icon-s-repost"
}
- Text {
+ SocialReshareText {
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
-
+ 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"
@@ -157,61 +165,18 @@ SocialMediaFeedItem {
plainText: item._bodyText
}
- Row {
+ SocialPostMetadataRow {
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
- }
+ 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 {
@@ -227,39 +192,11 @@ SocialMediaFeedItem {
}
}
- 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")
+ return model && model.mastodonId ? model.mastodonId.toString() : ""
}
function actionAccountId() {
@@ -268,7 +205,7 @@ SocialMediaFeedItem {
}
function shareStatusUrl() {
- return item.stringValue("url", "link", "uri")
+ return model && model.url ? model.url.toString() : ""
}
function topLevelParent() {
@@ -327,141 +264,78 @@ SocialMediaFeedItem {
Component {
id: actionMenuComponent
- ContextMenu {
+ SocialInteractionContextMenu {
id: actionMenu
- property bool menuOpen: height > 0
- property bool wasOpened: false
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
+ }
+ ]
- onPositionChanged: {
- horizontalActions.xPos = _contentColumn.mapFromItem(actionMenu, mouse.x, mouse.y).x
+ onInteractionMenuOpened: item._contextMenuOpen = true
+ onInteractionMenuClosed: {
+ item._contextMenuOpen = false
+ destroy()
+ item._actionMenu = null
}
- onMenuOpenChanged: {
- if (menuOpen) {
- wasOpened = true
- item._contextMenuOpen = true
- } else if (wasOpened) {
- item._contextMenuOpen = false
- destroy()
- item._actionMenu = null
+ onInteractionTriggered: function(actionName) {
+ if (!actionEnabled) {
+ return
}
- }
-
- 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)
- }
+ var postId = item.actionPostId()
+ var accountId = item.actionAccountId()
+ if (actionName === "like") {
+ if (item.isFavourited) {
+ item.postActions.unfavourite(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()
+ item.postActions.favourite(accountId, postId)
}
- }
-
- 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)
+ } else if (actionName === "reblog") {
+ if (item.isReblogged) {
+ item.postActions.unboost(accountId, postId)
+ } else {
+ item.postActions.boost(accountId, postId)
}
-
- 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)
+ } 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()
}
}
}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro b/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro
index 04be215..8c9663e 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro
+++ b/eventsview-plugins/eventsview-plugin-mastodon/eventsview-plugin-mastodon.pro
@@ -9,6 +9,7 @@ TARGET = $$qtLibraryTarget($$TARGET)
MODULENAME = com/jolla/eventsview/mastodon
TARGETPATH = $$[QT_INSTALL_QML]/$$MODULENAME
+QT -= gui
QT += qml network
CONFIG += plugin link_pkgconfig
PKGCONFIG += socialcache accounts-qt5 libsignon-qt5 sailfishaccounts
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml b/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
index fac0b89..2a5d9c1 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
+++ b/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
@@ -13,6 +13,7 @@ import "shared"
SocialMediaAccountDelegate {
id: delegateItem
+ property string instanceHomeUrl: ""
//: Mastodon posts
//% "Posts"
@@ -21,39 +22,35 @@ SocialMediaAccountDelegate {
showRemainingCount: false
services: ["Posts"]
- socialNetwork: 9
+ socialNetwork: SocialSync.Mastodon
dataType: SocialSync.Posts
providerName: "mastodon"
+ periodicSyncLoopEnabled: true
MastodonPostActions {
id: mastodonPostActions
}
- model: MastodonPostsModel {
- onCountChanged: {
- if (count > 0) {
- if (!updateTimer.running) {
- shortUpdateTimer.start()
- }
- } else {
- shortUpdateTimer.stop()
- }
- }
- }
+ model: MastodonPostsModel {}
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"])
+ imageList: model.images
+ avatarSource: model.icon
+ fallbackAvatarSource: model.icon
resolvedStatusUrl: delegateItem.authorizeInteractionUrl(model)
- postId: delegateItem.stringRole(model, ["mastodonId", "statusId", "id", "twitterId"])
+ postId: model.mastodonId
postActions: mastodonPostActions
- accountId: delegateItem.firstAccountId(model)
+ accountId: delegateItem.firstAccountId(model, -1)
- onTriggered: Qt.openUrlExternally(resolvedStatusUrl)
+ onTriggered: {
+ if (resolvedStatusUrl.length > 0) {
+ Qt.openUrlExternally(resolvedStatusUrl)
+ }
+ }
Component.onCompleted: {
+ delegateItem.instanceHomeUrl = statusUrl({instanceUrl: model.instanceUrl})
refreshTimeCount = Qt.binding(function() { return delegateItem.refreshTimeCount })
connectedToNetwork = Qt.binding(function() { return delegateItem.connectedToNetwork })
eventsColumnMaxWidth = Qt.binding(function() { return delegateItem.eventsColumnMaxWidth })
@@ -62,101 +59,61 @@ SocialMediaAccountDelegate {
//% "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")
+ 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 && !updateTimer.running) {
- shortUpdateTimer.start()
+ if (delegateItem.hasSyncableAccounts) {
+ delegateItem.startPeriodicSyncLoop()
}
} else {
- shortUpdateTimer.stop()
+ delegateItem.stopPeriodicSyncLoop()
}
}
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()
+ delegateItem.startPeriodicSyncLoop()
}
}
- Timer {
- id: updateTimer
+ Connections {
+ target: delegateItem.model
- 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
+ onCountChanged: {
+ if (target.count === 0) {
+ delegateItem.instanceHomeUrl = ""
}
}
- return ""
}
function statusUrl(modelData) {
- var directUrl = stringRole(modelData, ["url", "link", "uri"])
+ var directUrl = modelData && modelData.url ? modelData.url.toString() : ""
if (directUrl.length > 0) {
return directUrl
}
- var instanceUrl = stringRole(modelData, ["instanceUrl", "serverUrl", "baseUrl"])
+ var instanceUrl = modelData && modelData.instanceUrl ? modelData.instanceUrl.toString() : ""
+ instanceUrl = stripTrailingSlashes(instanceUrl)
if (instanceUrl.length === 0) {
- instanceUrl = "https://mastodon.social"
- }
- while (instanceUrl.length > 0 && instanceUrl.charAt(instanceUrl.length - 1) === "/") {
- instanceUrl = instanceUrl.slice(0, instanceUrl.length - 1)
+ return ""
}
- var accountName = stringRole(modelData, ["accountName", "acct", "screenName", "username"])
- var statusId = stringRole(modelData, ["mastodonId", "statusId", "id", "twitterId"])
+ var accountName = modelData && modelData.accountName ? modelData.accountName.toString() : ""
+ var statusId = modelData && modelData.mastodonId ? modelData.mastodonId.toString() : ""
if (accountName.length > 0 && statusId.length > 0) {
- while (accountName.length > 0 && accountName.charAt(0) === "@") {
- accountName = accountName.substring(1)
- }
+ accountName = trimLeadingCharacter(accountName, "@")
return instanceUrl + "/@" + accountName + "/" + statusId
}
@@ -169,13 +126,11 @@ SocialMediaAccountDelegate {
return targetUrl
}
- var instanceUrl = stringRole(modelData, ["instanceUrl", "serverUrl", "baseUrl"])
+ var instanceUrl = modelData && modelData.instanceUrl ? modelData.instanceUrl.toString() : ""
if (instanceUrl.length === 0) {
return targetUrl
}
- while (instanceUrl.length > 0 && instanceUrl.charAt(instanceUrl.length - 1) === "/") {
- instanceUrl = instanceUrl.slice(0, instanceUrl.length - 1)
- }
+ instanceUrl = stripTrailingSlashes(instanceUrl)
// Links on the user's own instance should open directly.
var sameServer = /^([a-z][a-z0-9+.-]*):\/\/([^\/?#]+)/i
@@ -192,23 +147,34 @@ SocialMediaAccountDelegate {
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.")
+ 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
}
- return source
+
+ var accountId = Number(accounts[0])
+ return isNaN(accountId) ? fallback : accountId
}
- function firstAccountId(modelData) {
- var accounts = modelData.accounts
- if (accounts && accounts.length > 0) {
- var accountId = Number(accounts[0])
- if (!isNaN(accountId)) {
- return 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 -1
+ return value
}
}
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp b/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp
index aa98a95..fe61320 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp
+++ b/eventsview-plugins/eventsview-plugin-mastodon/mastodonpostsmodel.cpp
@@ -17,31 +17,7 @@
*/
#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;
-}
-
-}
+#include <socialcache/socialposthelpers.h>
MastodonPostsModel::MastodonPostsModel(QObject *parent)
: QAbstractListModel(parent)
@@ -123,13 +99,16 @@ void MastodonPostsModel::postsChanged()
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());
+ SocialPostHelpers::appendCommonPostFields(&eventMap, post,
+ MastodonPostsModel::MastodonId,
+ MastodonPostsModel::Name,
+ MastodonPostsModel::Body,
+ MastodonPostsModel::Timestamp,
+ MastodonPostsModel::Icon,
+ MastodonPostsModel::Images,
+ MastodonPostsModel::Accounts);
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);
@@ -140,18 +119,6 @@ void MastodonPostsModel::postsChanged()
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);
}
diff --git a/rpm/sailfish-account-mastodon.spec b/rpm/sailfish-account-mastodon.spec
index 97e790a..c321184 100644
--- a/rpm/sailfish-account-mastodon.spec
+++ b/rpm/sailfish-account-mastodon.spec
@@ -21,6 +21,7 @@ BuildRequires: pkgconfig(buteosyncfw5) >= 0.10.0
BuildRequires: pkgconfig(accounts-qt5)
BuildRequires: pkgconfig(libsignon-qt5)
BuildRequires: pkgconfig(socialcache)
+BuildRequires: pkgconfig(buteosocialcommon)
BuildRequires: pkgconfig(libsailfishkeyprovider)
BuildRequires: pkgconfig(sailfishaccounts)
BuildRequires: pkgconfig(nemotransferengine-qt5) >= 2.0.0
@@ -28,9 +29,10 @@ BuildRequires: pkgconfig(nemonotifications-qt5)
Requires: jolla-settings-accounts-extensions-onlinesync
Requires: qmf-oauth2-plugin >= 0.0.7
Requires: buteo-syncfw-qt5-msyncd
+Requires: buteo-sync-plugins-social >= 0.4.0
Requires: systemd
Requires: lipstick-jolla-home-qt5-components >= 1.2.50
-Requires: eventsview-extensions
+Requires: eventsview-extensions >= 0.1.9-2
Requires: sailfishsilica-qt5 >= 1.1.108
Requires: declarative-transferengine-qt5 >= 0.3.13
Requires: nemo-transferengine-qt5 >= 2.0.0
@@ -74,8 +76,6 @@ fi
%license LICENSES/LGPL-2.1-or-later.txt
%{_libdir}/libmastodoncommon.so.*
%exclude %{_libdir}/libmastodoncommon.so
-%{_libdir}/libmastodonbuteocommon.so.*
-%exclude %{_libdir}/libmastodonbuteocommon.so
%{_datadir}/accounts/providers/mastodon.provider
%{_datadir}/accounts/services/mastodon-microblog.service
%{_datadir}/accounts/services/mastodon-notifications.service
diff --git a/settings/accounts-translations-plugin/accounts-translations-plugin.pro b/settings/accounts-translations-plugin/accounts-translations-plugin.pro
index aad978f..8a87592 100644
--- a/settings/accounts-translations-plugin/accounts-translations-plugin.pro
+++ b/settings/accounts-translations-plugin/accounts-translations-plugin.pro
@@ -9,6 +9,7 @@ TARGET = $$qtLibraryTarget($$TARGET)
MODULENAME = com/jolla/settings/accounts/mastodon
TARGETPATH = $$[QT_INSTALL_QML]/$$MODULENAME
+QT -= gui
QT += qml
CONFIG += plugin
diff --git a/transferengine-plugins/mastodonshareplugin/mastodonshareplugin.pro b/transferengine-plugins/mastodonshareplugin/mastodonshareplugin.pro
index 59fb7e1..011bcc5 100644
--- a/transferengine-plugins/mastodonshareplugin/mastodonshareplugin.pro
+++ b/transferengine-plugins/mastodonshareplugin/mastodonshareplugin.pro
@@ -9,6 +9,8 @@ DEPENDPATH += .
INCLUDEPATH += ..
INCLUDEPATH += ../../common
+QT -= gui
+
CONFIG += link_pkgconfig
PKGCONFIG += nemotransferengine-qt5 accounts-qt5 sailfishaccounts libsignon-qt5
diff --git a/transferengine-plugins/mastodontransferplugin/mastodontransferplugin.pro b/transferengine-plugins/mastodontransferplugin/mastodontransferplugin.pro
index 422a889..2457a7a 100644
--- a/transferengine-plugins/mastodontransferplugin/mastodontransferplugin.pro
+++ b/transferengine-plugins/mastodontransferplugin/mastodontransferplugin.pro
@@ -9,6 +9,7 @@ DEPENDPATH += .
INCLUDEPATH += ..
INCLUDEPATH += ../../common
+QT -= gui
QT += network
CONFIG += link_pkgconfig