mirror of https://github.com/KDE/kasts.git
A lot of progress on refactoring with DataManager
This commit is contained in:
parent
8035f0fd60
commit
13868709e7
|
@ -119,86 +119,3 @@ void Database::cleanup()
|
|||
// TODO: also delete enclosures and authors(?)
|
||||
}
|
||||
}
|
||||
|
||||
bool Database::feedExists(const QString &url)
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (url) FROM Feeds WHERE url=:url;"));
|
||||
query.bindValue(QStringLiteral(":url"), url);
|
||||
Database::instance().execute(query);
|
||||
query.next();
|
||||
return query.value(0).toInt() != 0;
|
||||
}
|
||||
|
||||
void Database::addFeed(const QString &url)
|
||||
{
|
||||
qDebug() << "Adding feed";
|
||||
if (feedExists(url)) {
|
||||
qDebug() << "Feed already exists";
|
||||
return;
|
||||
}
|
||||
qDebug() << "Feed does not yet exist";
|
||||
|
||||
QUrl urlFromInput = QUrl::fromUserInput(url);
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("INSERT INTO Feeds VALUES (:name, :url, :image, :link, :description, :deleteAfterCount, :deleteAfterType, :subscribed, :lastUpdated, :notify);"));
|
||||
query.bindValue(QStringLiteral(":name"), urlFromInput.toString());
|
||||
query.bindValue(QStringLiteral(":url"), urlFromInput.toString());
|
||||
query.bindValue(QStringLiteral(":image"), QLatin1String(""));
|
||||
query.bindValue(QStringLiteral(":link"), QLatin1String(""));
|
||||
query.bindValue(QStringLiteral(":description"), QLatin1String(""));
|
||||
query.bindValue(QStringLiteral(":deleteAfterCount"), 0);
|
||||
query.bindValue(QStringLiteral(":deleteAfterType"), 0);
|
||||
query.bindValue(QStringLiteral(":subscribed"), QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||
query.bindValue(QStringLiteral(":lastUpdated"), 0);
|
||||
query.bindValue(QStringLiteral(":notify"), false);
|
||||
execute(query);
|
||||
|
||||
Q_EMIT feedAdded(urlFromInput.toString());
|
||||
|
||||
Fetcher::instance().fetch(urlFromInput.toString());
|
||||
}
|
||||
|
||||
void Database::importFeeds(const QString &path)
|
||||
{
|
||||
QUrl url(path);
|
||||
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
QXmlStreamReader xmlReader(&file);
|
||||
while(!xmlReader.atEnd()) {
|
||||
xmlReader.readNext();
|
||||
if(xmlReader.tokenType() == 4 && xmlReader.attributes().hasAttribute(QStringLiteral("xmlUrl"))) {
|
||||
addFeed(xmlReader.attributes().value(QStringLiteral("xmlUrl")).toString());
|
||||
}
|
||||
}
|
||||
Fetcher::instance().fetchAll();
|
||||
}
|
||||
|
||||
void Database::exportFeeds(const QString &path)
|
||||
{
|
||||
QUrl url(path);
|
||||
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||
file.open(QIODevice::WriteOnly);
|
||||
|
||||
QXmlStreamWriter xmlWriter(&file);
|
||||
xmlWriter.setAutoFormatting(true);
|
||||
xmlWriter.writeStartDocument(QStringLiteral("1.0"));
|
||||
xmlWriter.writeStartElement(QStringLiteral("opml"));
|
||||
xmlWriter.writeEmptyElement(QStringLiteral("head"));
|
||||
xmlWriter.writeStartElement(QStringLiteral("body"));
|
||||
xmlWriter.writeAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT url, name FROM Feeds;"));
|
||||
execute(query);
|
||||
while(query.next()) {
|
||||
xmlWriter.writeEmptyElement(QStringLiteral("outline"));
|
||||
xmlWriter.writeAttribute(QStringLiteral("xmlUrl"), query.value(0).toString());
|
||||
xmlWriter.writeAttribute(QStringLiteral("title"), query.value(1).toString());
|
||||
}
|
||||
xmlWriter.writeEndElement();
|
||||
xmlWriter.writeEndElement();
|
||||
xmlWriter.writeEndDocument();
|
||||
|
||||
}
|
||||
|
|
|
@ -20,12 +20,6 @@ public:
|
|||
}
|
||||
bool execute(QSqlQuery &query);
|
||||
bool execute(const QString &query);
|
||||
Q_INVOKABLE void addFeed(const QString &url);
|
||||
Q_INVOKABLE void importFeeds(const QString &path);
|
||||
Q_INVOKABLE void exportFeeds(const QString &path);
|
||||
|
||||
Q_SIGNALS:
|
||||
void feedAdded(const QString &url);
|
||||
|
||||
private:
|
||||
Database();
|
||||
|
@ -34,5 +28,4 @@ private:
|
|||
bool migrate();
|
||||
bool migrateTo1();
|
||||
void cleanup();
|
||||
bool feedExists(const QString &url);
|
||||
};
|
||||
|
|
|
@ -4,16 +4,36 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlError>
|
||||
#include <QStandardPaths>
|
||||
#include <QUrl>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include "datamanager.h"
|
||||
#include "fetcher.h"
|
||||
#include "database.h"
|
||||
|
||||
|
||||
DataManager::DataManager()
|
||||
{
|
||||
// connect signals to lambda slots
|
||||
connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, [this](const QString &url, const QString &name, const QString &image, const QString &link, const QString &description, const QDateTime &lastUpdated) {
|
||||
m_feeds[url]->setName(name);
|
||||
m_feeds[url]->setImage(image);
|
||||
m_feeds[url]->setLink(link);
|
||||
m_feeds[url]->setDescription(description);
|
||||
m_feeds[url]->setLastUpdated(lastUpdated);
|
||||
// TODO: signal feedmodel: Q_EMIT dataChanged(createIndex(i, 0), createIndex(i, 0));
|
||||
});
|
||||
connect(&Fetcher::instance(), &Fetcher::feedUpdated, this, [this](const QString &url) {
|
||||
// TODO: make DataManager rescan entries
|
||||
Q_EMIT feedEntriesUpdated(url);
|
||||
});
|
||||
|
||||
// Only read unique feedurls and entry ids from the database.
|
||||
// The feed and entry datastructres will be loaded lazily.
|
||||
// The feed and entry datastructures will be loaded lazily.
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT url FROM Feeds;"));
|
||||
Database::instance().execute(query);
|
||||
|
@ -28,8 +48,8 @@ DataManager::DataManager()
|
|||
Database::instance().execute(query);
|
||||
while (query.next()) {
|
||||
m_entrymap[feedurl] += query.value(QStringLiteral("id")).toString();
|
||||
qDebug() << m_entrymap[feedurl];
|
||||
}
|
||||
qDebug() << m_entrymap[feedurl];
|
||||
}
|
||||
qDebug() << m_entrymap;
|
||||
}
|
||||
|
@ -46,7 +66,195 @@ Feed* DataManager::getFeed(QString const feedurl) const
|
|||
return m_feeds[feedurl];
|
||||
}
|
||||
|
||||
void DataManager::loadFeed(QString const feedurl) const
|
||||
|
||||
Entry* DataManager::getEntry(int const feed_index, int const entry_index) const
|
||||
{
|
||||
return getEntry(m_entrymap[m_feedmap[feed_index]][entry_index]);
|
||||
}
|
||||
|
||||
Entry* DataManager::getEntry(const Feed* feed, int const entry_index) const
|
||||
{
|
||||
return getEntry(m_entrymap[feed->url()][entry_index]);
|
||||
}
|
||||
|
||||
Entry* DataManager::getEntry(QString id) const
|
||||
{
|
||||
if (m_entries[id] == nullptr)
|
||||
loadEntry(id);
|
||||
return m_entries[id];
|
||||
}
|
||||
|
||||
int DataManager::feedCount() const
|
||||
{
|
||||
return m_feedmap.count();
|
||||
}
|
||||
|
||||
int DataManager::entryCount(const int feed_index) const
|
||||
{
|
||||
return m_entrymap[m_feedmap[feed_index]].count();
|
||||
}
|
||||
|
||||
int DataManager::entryCount(const Feed* feed) const
|
||||
{
|
||||
return m_entrymap[feed->url()].count();
|
||||
}
|
||||
|
||||
int DataManager::unreadEntryCount(const Feed* feed) const
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND read=0;"));
|
||||
query.bindValue(QStringLiteral(":feed"), feed->url());
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
return -1;
|
||||
return query.value(0).toInt();
|
||||
}
|
||||
|
||||
void DataManager::removeFeed(const Feed* feed)
|
||||
{
|
||||
removeFeed(m_feedmap.indexOf(feed->url()));
|
||||
}
|
||||
|
||||
void DataManager::removeFeed(const int &index)
|
||||
{
|
||||
// Get feed pointer
|
||||
Feed* feed = m_feeds[m_feedmap[index]];
|
||||
|
||||
// First delete everything from the database
|
||||
|
||||
// Delete Authors
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("DELETE FROM Authors WHERE feed=:feed;"));
|
||||
query.bindValue(QStringLiteral(":feed"), feed->url());
|
||||
Database::instance().execute(query);
|
||||
|
||||
// Delete Entries
|
||||
query.prepare(QStringLiteral("DELETE FROM Entries WHERE feed=:feed;"));
|
||||
query.bindValue(QStringLiteral(":feed"), feed->url());
|
||||
Database::instance().execute(query);
|
||||
|
||||
// Delete Enclosures
|
||||
query.prepare(QStringLiteral("DELETE FROM Enclosures WHERE feed=:feed;"));
|
||||
query.bindValue(QStringLiteral(":feed"), feed->url());
|
||||
Database::instance().execute(query);
|
||||
|
||||
// Delete Feed
|
||||
query.prepare(QStringLiteral("DELETE FROM Feeds WHERE url=:url;"));
|
||||
query.bindValue(QStringLiteral(":url"), feed->url());
|
||||
Database::instance().execute(query);
|
||||
|
||||
// Then delete the instances and mappings
|
||||
for (auto& id : m_entrymap[feed->url()]) {
|
||||
delete m_entries[id]; // delete pointer
|
||||
m_entries.remove(id); // delete the hash key
|
||||
}
|
||||
m_entrymap.remove(feed->url()); // remove all the entry mappings belonging to the feed
|
||||
|
||||
delete feed; // remove the pointer
|
||||
m_feeds.remove(m_feedmap[index]); // remove from m_feeds
|
||||
m_feedmap.removeAt(index); // remove from m_feedmap
|
||||
Q_EMIT(feedRemoved(index));
|
||||
}
|
||||
|
||||
void DataManager::addFeed(const QString &url)
|
||||
{
|
||||
qDebug() << "Adding feed";
|
||||
if (feedExists(url)) {
|
||||
qDebug() << "Feed already exists";
|
||||
return;
|
||||
}
|
||||
qDebug() << "Feed does not yet exist";
|
||||
|
||||
QUrl urlFromInput = QUrl::fromUserInput(url);
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("INSERT INTO Feeds VALUES (:name, :url, :image, :link, :description, :deleteAfterCount, :deleteAfterType, :subscribed, :lastUpdated, :notify);"));
|
||||
query.bindValue(QStringLiteral(":name"), urlFromInput.toString());
|
||||
query.bindValue(QStringLiteral(":url"), urlFromInput.toString());
|
||||
query.bindValue(QStringLiteral(":image"), QLatin1String(""));
|
||||
query.bindValue(QStringLiteral(":link"), QLatin1String(""));
|
||||
query.bindValue(QStringLiteral(":description"), QLatin1String(""));
|
||||
query.bindValue(QStringLiteral(":deleteAfterCount"), 0);
|
||||
query.bindValue(QStringLiteral(":deleteAfterType"), 0);
|
||||
query.bindValue(QStringLiteral(":subscribed"), QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||
query.bindValue(QStringLiteral(":lastUpdated"), 0);
|
||||
query.bindValue(QStringLiteral(":notify"), false);
|
||||
Database::instance().execute(query);
|
||||
|
||||
m_feeds[urlFromInput.toString()] = new Feed(urlFromInput.toString());
|
||||
m_feedmap.append(urlFromInput.toString());
|
||||
|
||||
Q_EMIT feedAdded(urlFromInput.toString());
|
||||
|
||||
Fetcher::instance().fetch(urlFromInput.toString());
|
||||
}
|
||||
|
||||
void DataManager::importFeeds(const QString &path)
|
||||
{
|
||||
QUrl url(path);
|
||||
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
QXmlStreamReader xmlReader(&file);
|
||||
while(!xmlReader.atEnd()) {
|
||||
xmlReader.readNext();
|
||||
if(xmlReader.tokenType() == 4 && xmlReader.attributes().hasAttribute(QStringLiteral("xmlUrl"))) {
|
||||
addFeed(xmlReader.attributes().value(QStringLiteral("xmlUrl")).toString());
|
||||
}
|
||||
}
|
||||
Fetcher::instance().fetchAll();
|
||||
}
|
||||
|
||||
void DataManager::exportFeeds(const QString &path)
|
||||
{
|
||||
QUrl url(path);
|
||||
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||
file.open(QIODevice::WriteOnly);
|
||||
|
||||
QXmlStreamWriter xmlWriter(&file);
|
||||
xmlWriter.setAutoFormatting(true);
|
||||
xmlWriter.writeStartDocument(QStringLiteral("1.0"));
|
||||
xmlWriter.writeStartElement(QStringLiteral("opml"));
|
||||
xmlWriter.writeEmptyElement(QStringLiteral("head"));
|
||||
xmlWriter.writeStartElement(QStringLiteral("body"));
|
||||
xmlWriter.writeAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT url, name FROM Feeds;"));
|
||||
Database::instance().execute(query);
|
||||
while(query.next()) {
|
||||
xmlWriter.writeEmptyElement(QStringLiteral("outline"));
|
||||
xmlWriter.writeAttribute(QStringLiteral("xmlUrl"), query.value(0).toString());
|
||||
xmlWriter.writeAttribute(QStringLiteral("title"), query.value(1).toString());
|
||||
}
|
||||
xmlWriter.writeEndElement();
|
||||
xmlWriter.writeEndElement();
|
||||
xmlWriter.writeEndDocument();
|
||||
|
||||
}
|
||||
|
||||
void DataManager::loadFeed(const QString feedurl) const
|
||||
{
|
||||
m_feeds[feedurl] = new Feed(feedurl);
|
||||
}
|
||||
|
||||
void DataManager::loadEntry(const QString id) const
|
||||
{
|
||||
// First find the feed that this entry belongs to
|
||||
Feed* feed = nullptr;
|
||||
QHashIterator<QString, QStringList> i(m_entrymap);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value().contains(id))
|
||||
feed = getFeed(i.key());
|
||||
}
|
||||
if (feed == nullptr) {
|
||||
qDebug() << "Failed to find feed belonging to entry" << id;
|
||||
return;
|
||||
}
|
||||
m_entries[id] = new Entry(feed, id);
|
||||
}
|
||||
|
||||
bool DataManager::feedExists(const QString &url)
|
||||
{
|
||||
return m_feeds.contains(url);
|
||||
}
|
||||
|
|
|
@ -22,13 +22,39 @@ public:
|
|||
|
||||
Feed* getFeed(int const index) const;
|
||||
Feed* getFeed(QString const feedurl) const;
|
||||
Entry* getEntry(int const feed_index, int const entry_index) const;
|
||||
Entry* getEntry(const Feed* feed, int const entry_index) const;
|
||||
Entry* getEntry(const QString id) const;
|
||||
int feedCount() const;
|
||||
int entryCount(const int feed_index) const;
|
||||
int entryCount(const Feed* feed) const;
|
||||
int unreadEntryCount(const Feed* feed) const;
|
||||
Q_INVOKABLE void addFeed(const QString &url);
|
||||
Q_INVOKABLE void removeFeed(const Feed* feed);
|
||||
Q_INVOKABLE void removeFeed(const int &index);
|
||||
|
||||
//Q_INVOKABLE void addEntry(const QString &url);
|
||||
//Q_INVOKABLE void removeEntry(const QString &url);
|
||||
//Q_INVOKABLE void removeEntry(const Feed* feed, const int &index);
|
||||
|
||||
Q_INVOKABLE void importFeeds(const QString &path);
|
||||
Q_INVOKABLE void exportFeeds(const QString &path);
|
||||
|
||||
Q_SIGNALS:
|
||||
void feedAdded(const QString &url);
|
||||
void feedRemoved(const int &index);
|
||||
void entryAdded(const QString &id);
|
||||
void entryRemoved(const Feed*, const int &index);
|
||||
void feedEntriesUpdated(const QString &url);
|
||||
|
||||
private:
|
||||
DataManager();
|
||||
void loadFeed(QString feedurl) const;
|
||||
void loadEntry(QString id) const;
|
||||
bool feedExists(const QString &url);
|
||||
|
||||
QVector<QString> m_feedmap;
|
||||
QStringList m_feedmap;
|
||||
mutable QHash<QString, Feed*> m_feeds;
|
||||
QHash<QString, QVector<QString> > m_entrymap;
|
||||
QHash<QString, QStringList> m_entrymap;
|
||||
mutable QHash<QString, Entry*> m_entries;
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QVector>
|
||||
|
||||
#include "database.h"
|
||||
#include "datamanager.h"
|
||||
#include "entriesmodel.h"
|
||||
#include "fetcher.h"
|
||||
|
||||
|
@ -16,30 +17,20 @@ EntriesModel::EntriesModel(Feed *feed)
|
|||
: QAbstractListModel(feed)
|
||||
, m_feed(feed)
|
||||
{
|
||||
connect(&Fetcher::instance(), &Fetcher::feedUpdated, this, [this](const QString &url) {
|
||||
connect(&DataManager::instance(), &DataManager::feedEntriesUpdated, this, [this](const QString &url) {
|
||||
if (m_feed->url() == url) {
|
||||
beginResetModel();
|
||||
for (auto &entry : m_entries) {
|
||||
delete entry;
|
||||
}
|
||||
m_entries.clear();
|
||||
// TODO: make sure to pop the entrylistpage if it's the active one
|
||||
endResetModel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EntriesModel::~EntriesModel()
|
||||
{
|
||||
qDeleteAll(m_entries);
|
||||
}
|
||||
|
||||
QVariant EntriesModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role != 0)
|
||||
return QVariant();
|
||||
if (m_entries[index.row()] == nullptr)
|
||||
loadEntry(index.row());
|
||||
return QVariant::fromValue(m_entries[index.row()]);
|
||||
return QVariant::fromValue(DataManager::instance().getEntry(m_feed, index.row()));
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> EntriesModel::roleNames() const
|
||||
|
@ -52,18 +43,7 @@ QHash<int, QByteArray> EntriesModel::roleNames() const
|
|||
int EntriesModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT() FROM Entries WHERE feed=:feed;"));
|
||||
query.bindValue(QStringLiteral(":feed"), m_feed->url());
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
qWarning() << "Failed to query feed count";
|
||||
return query.value(0).toInt();
|
||||
}
|
||||
|
||||
void EntriesModel::loadEntry(int index) const
|
||||
{
|
||||
m_entries[index] = new Entry(m_feed, index);
|
||||
return DataManager::instance().entryCount(m_feed);
|
||||
}
|
||||
|
||||
Feed *EntriesModel::feed() const
|
||||
|
|
|
@ -22,7 +22,6 @@ class EntriesModel : public QAbstractListModel
|
|||
|
||||
public:
|
||||
explicit EntriesModel(Feed *feed);
|
||||
~EntriesModel() override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
|
@ -30,8 +29,5 @@ public:
|
|||
Feed *feed() const;
|
||||
|
||||
private:
|
||||
void loadEntry(int index) const;
|
||||
|
||||
Feed *m_feed;
|
||||
mutable QHash<int, Entry *> m_entries;
|
||||
};
|
||||
|
|
|
@ -47,6 +47,41 @@ Entry::Entry(Feed *feed, int index)
|
|||
}
|
||||
}
|
||||
|
||||
Entry::Entry(Feed *feed, QString id)
|
||||
: QObject(nullptr)
|
||||
, m_feed(feed)
|
||||
{
|
||||
QSqlQuery entryQuery;
|
||||
entryQuery.prepare(QStringLiteral("SELECT * FROM Entries WHERE feed=:feed AND id=:id;"));
|
||||
entryQuery.bindValue(QStringLiteral(":feed"), m_feed->url());
|
||||
entryQuery.bindValue(QStringLiteral(":id"), id);
|
||||
Database::instance().execute(entryQuery);
|
||||
if (!entryQuery.next())
|
||||
qWarning() << "No element with index" << id << "found in feed" << m_feed->url();
|
||||
|
||||
QSqlQuery authorQuery;
|
||||
authorQuery.prepare(QStringLiteral("SELECT * FROM Authors WHERE id=:id"));
|
||||
authorQuery.bindValue(QStringLiteral(":id"), entryQuery.value(QStringLiteral("id")).toString());
|
||||
Database::instance().execute(authorQuery);
|
||||
|
||||
while (authorQuery.next()) {
|
||||
m_authors += new Author(authorQuery.value(QStringLiteral("name")).toString(), authorQuery.value(QStringLiteral("email")).toString(), authorQuery.value(QStringLiteral("uri")).toString(), nullptr);
|
||||
}
|
||||
|
||||
m_created.setSecsSinceEpoch(entryQuery.value(QStringLiteral("created")).toInt());
|
||||
m_updated.setSecsSinceEpoch(entryQuery.value(QStringLiteral("updated")).toInt());
|
||||
|
||||
m_id = entryQuery.value(QStringLiteral("id")).toString();
|
||||
m_title = entryQuery.value(QStringLiteral("title")).toString();
|
||||
m_content = entryQuery.value(QStringLiteral("content")).toString();
|
||||
m_link = entryQuery.value(QStringLiteral("link")).toString();
|
||||
m_read = entryQuery.value(QStringLiteral("read")).toBool();
|
||||
|
||||
if (entryQuery.value(QStringLiteral("hasEnclosure")).toBool()) {
|
||||
m_enclosure = new Enclosure(this);
|
||||
}
|
||||
}
|
||||
|
||||
Entry::~Entry()
|
||||
{
|
||||
qDeleteAll(m_authors);
|
||||
|
|
|
@ -35,6 +35,7 @@ class Entry : public QObject
|
|||
|
||||
public:
|
||||
Entry(Feed *feed, int index);
|
||||
Entry(Feed *feed, QString id);
|
||||
~Entry();
|
||||
|
||||
QString id() const;
|
||||
|
|
105
src/feed.cpp
105
src/feed.cpp
|
@ -7,6 +7,7 @@
|
|||
#include <QVariant>
|
||||
|
||||
#include "database.h"
|
||||
#include "datamanager.h"
|
||||
#include "entriesmodel.h"
|
||||
#include "feed.h"
|
||||
#include "fetcher.h"
|
||||
|
@ -78,6 +79,73 @@ Feed::Feed(int index)
|
|||
m_entries = new EntriesModel(this);
|
||||
}
|
||||
|
||||
Feed::Feed(QString const feedurl)
|
||||
: QObject(nullptr)
|
||||
{
|
||||
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT * FROM Feeds WHERE url=:feedurl;"));
|
||||
query.bindValue(QStringLiteral(":feedurl"), feedurl);
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
qWarning() << "Failed to load feed" << feedurl;
|
||||
|
||||
QSqlQuery authorQuery;
|
||||
authorQuery.prepare(QStringLiteral("SELECT * FROM Authors WHERE id='' AND feed=:feed"));
|
||||
authorQuery.bindValue(QStringLiteral(":feed"), feedurl);
|
||||
Database::instance().execute(authorQuery);
|
||||
while (authorQuery.next()) {
|
||||
m_authors += new Author(authorQuery.value(QStringLiteral("name")).toString(), authorQuery.value(QStringLiteral("email")).toString(), authorQuery.value(QStringLiteral("uri")).toString(), nullptr);
|
||||
}
|
||||
|
||||
m_subscribed.setSecsSinceEpoch(query.value(QStringLiteral("subscribed")).toInt());
|
||||
|
||||
m_lastUpdated.setSecsSinceEpoch(query.value(QStringLiteral("lastUpdated")).toInt());
|
||||
|
||||
m_url = query.value(QStringLiteral("url")).toString();
|
||||
m_name = query.value(QStringLiteral("name")).toString();
|
||||
m_image = query.value(QStringLiteral("image")).toString();
|
||||
m_link = query.value(QStringLiteral("link")).toString();
|
||||
m_description = query.value(QStringLiteral("description")).toString();
|
||||
m_deleteAfterCount = query.value(QStringLiteral("deleteAfterCount")).toInt();
|
||||
m_deleteAfterType = query.value(QStringLiteral("deleteAfterType")).toInt();
|
||||
m_notify = query.value(QStringLiteral("notify")).toBool();
|
||||
|
||||
m_errorId = 0;
|
||||
m_errorString = QLatin1String("");
|
||||
|
||||
connect(&Fetcher::instance(), &Fetcher::startedFetchingFeed, this, [this](const QString &url) {
|
||||
if (url == m_url) {
|
||||
m_errorId = 0;
|
||||
m_errorString = QLatin1String("");
|
||||
setRefreshing(true);
|
||||
}
|
||||
});
|
||||
connect(&Fetcher::instance(), &Fetcher::feedUpdated, this, [this](const QString &url) {
|
||||
if (url == m_url) {
|
||||
setRefreshing(false);
|
||||
Q_EMIT entryCountChanged();
|
||||
Q_EMIT unreadEntryCountChanged();
|
||||
setErrorId(0);
|
||||
setErrorString(QLatin1String(""));
|
||||
}
|
||||
});
|
||||
connect(&Fetcher::instance(), &Fetcher::error, this, [this](const QString &url, int errorId, const QString &errorString) {
|
||||
if(url == m_url) {
|
||||
setErrorId(errorId);
|
||||
setErrorString(errorString);
|
||||
setRefreshing(false);
|
||||
}
|
||||
});
|
||||
|
||||
connect(&Fetcher::instance(), &Fetcher::downloadFinished, this, [this](QString url) {
|
||||
if(url == m_image)
|
||||
Q_EMIT imageChanged(url);
|
||||
});
|
||||
|
||||
m_entries = new EntriesModel(this);
|
||||
}
|
||||
|
||||
Feed::~Feed()
|
||||
{
|
||||
}
|
||||
|
@ -139,24 +207,12 @@ bool Feed::notify() const
|
|||
|
||||
int Feed::entryCount() const
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed;"));
|
||||
query.bindValue(QStringLiteral(":feed"), m_url);
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
return -1;
|
||||
return query.value(0).toInt();
|
||||
return DataManager::instance().entryCount(this);
|
||||
}
|
||||
|
||||
int Feed::unreadEntryCount() const
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND read=0;"));
|
||||
query.bindValue(QStringLiteral(":feed"), m_url);
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
return -1;
|
||||
return query.value(0).toInt();
|
||||
return DataManager::instance().unreadEntryCount(this);
|
||||
}
|
||||
|
||||
bool Feed::refreshing() const
|
||||
|
@ -250,24 +306,3 @@ void Feed::refresh()
|
|||
{
|
||||
Fetcher::instance().fetch(m_url);
|
||||
}
|
||||
|
||||
void Feed::remove()
|
||||
{
|
||||
// Delete Authors
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("DELETE FROM Authors WHERE feed=:feed;"));
|
||||
query.bindValue(QStringLiteral(":feed"), m_url);
|
||||
Database::instance().execute(query);
|
||||
|
||||
// Delete Entries
|
||||
query.prepare(QStringLiteral("DELETE FROM Entries WHERE feed=:feed;"));
|
||||
query.bindValue(QStringLiteral(":feed"), m_url);
|
||||
Database::instance().execute(query);
|
||||
|
||||
// TODO Delete Enclosures
|
||||
|
||||
// Delete Feed
|
||||
query.prepare(QStringLiteral("DELETE FROM Feeds WHERE url=:url;"));
|
||||
query.bindValue(QStringLiteral(":url"), m_url);
|
||||
Database::instance().execute(query);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ class Feed : public QObject
|
|||
|
||||
public:
|
||||
Feed(int index);
|
||||
Feed(QString const feedurl);
|
||||
|
||||
~Feed();
|
||||
|
||||
|
@ -74,7 +75,6 @@ public:
|
|||
void setErrorString(const QString &errorString);
|
||||
|
||||
Q_INVOKABLE void refresh();
|
||||
void remove();
|
||||
|
||||
Q_SIGNALS:
|
||||
void nameChanged(const QString &name);
|
||||
|
|
|
@ -11,28 +11,20 @@
|
|||
#include <QVariant>
|
||||
|
||||
#include "database.h"
|
||||
#include "datamanager.h"
|
||||
#include "feedsmodel.h"
|
||||
#include "fetcher.h"
|
||||
|
||||
FeedsModel::FeedsModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
connect(&Database::instance(), &Database::feedAdded, this, [this]() {
|
||||
connect(&DataManager::instance(), &DataManager::feedAdded, this, [this]() {
|
||||
beginInsertRows(QModelIndex(), rowCount(QModelIndex()) - 1, rowCount(QModelIndex()) - 1);
|
||||
endInsertRows();
|
||||
});
|
||||
connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, [this](const QString &url, const QString &name, const QString &image, const QString &link, const QString &description, const QDateTime &lastUpdated) {
|
||||
for (int i = 0; i < m_feeds.length(); i++) {
|
||||
if (m_feeds[i]->url() == url) {
|
||||
m_feeds[i]->setName(name);
|
||||
m_feeds[i]->setImage(image);
|
||||
m_feeds[i]->setLink(link);
|
||||
m_feeds[i]->setDescription(description);
|
||||
m_feeds[i]->setLastUpdated(lastUpdated);
|
||||
Q_EMIT dataChanged(createIndex(i, 0), createIndex(i, 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
connect(&DataManager::instance(), &DataManager::feedRemoved, this, [this](const int &index) {
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
endRemoveRows();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,41 +37,25 @@ QHash<int, QByteArray> FeedsModel::roleNames() const
|
|||
|
||||
int FeedsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
QSqlQuery query;
|
||||
query.prepare(QStringLiteral("SELECT COUNT() FROM Feeds;"));
|
||||
Database::instance().execute(query);
|
||||
if (!query.next())
|
||||
qWarning() << "Failed to query feed count";
|
||||
return query.value(0).toInt();
|
||||
Q_UNUSED(parent);
|
||||
return DataManager::instance().feedCount();
|
||||
}
|
||||
|
||||
QVariant FeedsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role != 0)
|
||||
return QVariant();
|
||||
if (m_feeds.length() <= index.row())
|
||||
loadFeed(index.row());
|
||||
return QVariant::fromValue(m_feeds[index.row()]);
|
||||
}
|
||||
|
||||
void FeedsModel::loadFeed(int index) const
|
||||
{
|
||||
m_feeds += new Feed(index);
|
||||
return QVariant::fromValue(DataManager::instance().getFeed(index.row()));
|
||||
}
|
||||
|
||||
void FeedsModel::removeFeed(int index)
|
||||
{
|
||||
m_feeds[index]->remove();
|
||||
delete m_feeds[index];
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_feeds.removeAt(index);
|
||||
endRemoveRows();
|
||||
DataManager::instance().removeFeed(index);
|
||||
}
|
||||
|
||||
void FeedsModel::refreshAll()
|
||||
{
|
||||
for (auto &feed : m_feeds) {
|
||||
feed->refresh();
|
||||
}
|
||||
// for (auto &feed : m_feeds) {
|
||||
// feed->refresh();
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -24,9 +24,4 @@ public:
|
|||
int rowCount(const QModelIndex &parent) const override;
|
||||
Q_INVOKABLE void removeFeed(int index);
|
||||
Q_INVOKABLE void refreshAll();
|
||||
|
||||
private:
|
||||
void loadFeed(int index) const;
|
||||
|
||||
mutable QVector<Feed *> m_feeds;
|
||||
};
|
||||
|
|
|
@ -89,6 +89,8 @@ int main(int argc, char *argv[])
|
|||
Database::instance();
|
||||
|
||||
DataManager::instance();
|
||||
//qDebug() << DataManager::instance().getFeed(0)->name();
|
||||
//qDebug() << DataManager::instance().getEntry(0, 0)->title();
|
||||
|
||||
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ Kirigami.OverlaySheet {
|
|||
text: i18n("Add Feed")
|
||||
enabled: urlField.text
|
||||
onClicked: {
|
||||
Database.addFeed(urlField.text)
|
||||
DataManager.addFeed(urlField.text)
|
||||
addSheet.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ Kirigami.ScrollablePage {
|
|||
title: i18n("Import Feeds")
|
||||
folder: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
nameFilters: [i18n("All Files (*)"), i18n("XML Files (*.xml)"), i18n("OPML Files (*.opml)")]
|
||||
onAccepted: Database.importFeeds(file)
|
||||
onAccepted: DataManager.importFeeds(file)
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
|
@ -89,7 +89,7 @@ Kirigami.ScrollablePage {
|
|||
title: i18n("Export Feeds")
|
||||
folder: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
nameFilters: [i18n("All Files")]
|
||||
onAccepted: Database.exportFeeds(file)
|
||||
onAccepted: DataManager.exportFeeds(file)
|
||||
fileMode: FileDialog.SaveFile
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue