Refactor models

This commit is contained in:
Tobias Fella 2020-05-26 16:32:07 +02:00
parent 8a05a47e40
commit 8269cb960f
19 changed files with 585 additions and 183 deletions

View File

@ -4,6 +4,9 @@ set(alligator_SRCS
entryListModel.cpp
fetcher.cpp
database.cpp
entry.cpp
feed.cpp
author.cpp
resources.qrc
)

48
src/author.cpp Normal file
View File

@ -0,0 +1,48 @@
/**
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "author.h"
Author::Author(QString name, QString email, QString url, QObject *parent)
: QObject(parent)
, m_name(name)
, m_email(email)
, m_url(url)
{
}
Author::~Author()
{
}
QString Author::name() const
{
return m_name;
}
QString Author::email() const
{
return m_email;
}
QString Author::url() const
{
return m_url;
}

48
src/author.h Normal file
View File

@ -0,0 +1,48 @@
/**
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef AUTHOR_H
#define AUTHOR_H
#include <QObject>
class Author : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString email READ email CONSTANT)
Q_PROPERTY(QString url READ url CONSTANT)
public:
Author(QString name, QString email, QString url, QObject *parent = nullptr);
~Author();
QString name() const;
QString email() const;
QString url() const;
private:
QString m_name;
QString m_email;
QString m_url;
};
#endif // AUTHOR_H

View File

@ -151,5 +151,7 @@ void Database::addFeed(QString url)
query.bindValue(QStringLiteral(":image"), QLatin1String(""));
execute(query);
Fetcher::instance().fetch(QUrl(url));
Q_EMIT feedAdded(url);
Fetcher::instance().fetch(url);
}

View File

@ -22,8 +22,10 @@
#include <QSqlQuery>
class Database
class Database : public QObject
{
Q_OBJECT
public:
static Database &instance()
{
@ -32,7 +34,10 @@ public:
}
bool execute(QSqlQuery &query);
bool execute(QString query);
void addFeed(QString url);
Q_INVOKABLE void addFeed(QString url);
Q_SIGNALS:
void feedAdded(QString url);
private:
Database();

72
src/entry.cpp Normal file
View File

@ -0,0 +1,72 @@
/**
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "entry.h"
#include <QUrl>
Entry::Entry(QString title, QString content, QVector<Author *> authors, QDateTime created, QDateTime updated, QString link, QObject *parent)
: QObject(parent)
, m_title(title)
, m_content(content)
, m_authors(authors)
, m_created(created)
, m_updated(updated)
, m_link(link)
{
}
Entry::~Entry()
{
}
QString Entry::title() const
{
return m_title;
}
QString Entry::content() const
{
return m_content;
}
QVector<Author *> Entry::authors() const
{
return m_authors;
}
QDateTime Entry::created() const
{
return m_created;
}
QDateTime Entry::updated() const
{
return m_updated;
}
QString Entry::link() const
{
return m_link;
}
QString Entry::baseUrl() const
{
return QUrl(m_link).adjusted(QUrl::RemovePath).toString();
}

67
src/entry.h Normal file
View File

@ -0,0 +1,67 @@
/**
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef ENTRY_H
#define ENTRY_H
#include <QDateTime>
#include <QDebug>
#include <QObject>
#include <QString>
#include <QStringList>
#include "author.h"
#include "feed.h"
class Entry : public QObject
{
Q_OBJECT
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(QString content READ content CONSTANT)
Q_PROPERTY(QVector<Author *> authors READ authors CONSTANT)
Q_PROPERTY(QDateTime created READ created CONSTANT)
Q_PROPERTY(QDateTime updated READ updated CONSTANT)
Q_PROPERTY(QString link READ link CONSTANT)
Q_PROPERTY(QString baseUrl READ baseUrl CONSTANT)
public:
Entry(QString title, QString content, QVector<Author *> authors, QDateTime created, QDateTime updated, QString link, QObject *parent = nullptr);
~Entry();
QString title() const;
QString content() const;
QVector<Author *> authors() const;
QDateTime created() const;
QDateTime updated() const;
QString link() const;
QString baseUrl() const;
private:
Feed *m_feed;
QString m_title;
QString m_content;
QVector<Author *> m_authors;
QDateTime m_created;
QDateTime m_updated;
QString m_link;
};
#endif // ENTRY_H

View File

@ -18,8 +18,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <QDateTime>
#include <QSqlQuery>
#include <QAbstractListModel>
#include <QVariant>
#include <QVector>
#include "database.h"
@ -27,84 +27,90 @@
#include "fetcher.h"
EntryListModel::EntryListModel(QObject *parent)
: QSqlTableModel(parent)
: QAbstractListModel(parent)
{
setTable(QStringLiteral("entries"));
setSort(Updated, Qt::DescendingOrder);
setEditStrategy(OnFieldChange);
select();
connect(&Fetcher::instance(), &Fetcher::updated, this, [this]() { select(); });
connect(&Fetcher::instance(), &Fetcher::feedUpdated, this, [this](QString url) {
if (m_feed->url() == url) {
beginResetModel();
for (auto &entry : m_entries) {
delete entry;
}
m_entries.clear();
endResetModel();
}
});
connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, [this](QString url, QString name, QString image) {
if (m_feed->url() == url) {
m_feed->setName(name);
m_feed->setImage(image);
}
});
}
QVariant EntryListModel::data(const QModelIndex &index, int role) const
{
if (role == Enclosure) {
return enclosure(data(index, Id).toString());
}
if (role == Authors) {
QSqlQuery query;
query.prepare(QStringLiteral("SELECT name FROM Authors WHERE id=:id"));
query.bindValue(QStringLiteral(":id"), data(index, Id));
Database::instance().execute(query);
QStringList authors;
while (query.next()) {
authors += query.value(0).toString();
}
return authors;
}
if (role == Updated || role == Created) {
QDateTime updated;
updated.setSecsSinceEpoch(QSqlTableModel::data(createIndex(index.row(), role), 0).toInt());
return updated;
}
return QSqlTableModel::data(createIndex(index.row(), role), 0);
}
QString EntryListModel::enclosure(QString id) const
{
QSqlQuery query;
query.prepare(QStringLiteral("SELECT url from Enclosures WHERE id=:id;"));
query.bindValue(QStringLiteral(":id"), id);
Database::instance().execute(query);
return query.next() ? query.value(0).toString() : QLatin1String("");
if (role != 0)
return QVariant();
if (m_entries[index.row()] == nullptr)
loadEntry(index.row());
return QVariant::fromValue(m_entries[index.row()]);
}
QHash<int, QByteArray> EntryListModel::roleNames() const
{
QHash<int, QByteArray> roleNames;
roleNames[Feed] = "feed";
roleNames[Id] = "id";
roleNames[Title] = "title";
roleNames[Content] = "content";
roleNames[Created] = "created";
roleNames[Updated] = "updated";
roleNames[Link] = "link";
roleNames[Authors] = "authors";
roleNames[Enclosure] = "enclosure";
roleNames[0] = "entry";
return roleNames;
}
void EntryListModel::setFeed(QString url)
int EntryListModel::rowCount(const QModelIndex &parent) const
{
m_feed = url;
setFilter(QStringLiteral("feed ='%1'").arg(url));
select();
emit feedChanged(url);
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();
}
QString EntryListModel::feed() const
void EntryListModel::loadEntry(int index) const
{
QSqlQuery entryQuery;
entryQuery.prepare(QStringLiteral("SELECT * FROM Entries WHERE feed=:feed ORDER BY updated DESC LIMIT 1 OFFSET :index;"));
entryQuery.bindValue(QStringLiteral(":feed"), m_feed->url());
entryQuery.bindValue(QStringLiteral(":index"), index);
Database::instance().execute(entryQuery);
if (!entryQuery.next())
qWarning() << "No element with index" << index << "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);
QVector<Author *> authors;
while (authorQuery.next()) {
authors += new Author(authorQuery.value(QStringLiteral("name")).toString(), authorQuery.value(QStringLiteral("email")).toString(), authorQuery.value(QStringLiteral("uri")).toString(), nullptr);
}
QDateTime created;
created.setSecsSinceEpoch(entryQuery.value(QStringLiteral("created")).toInt());
QDateTime updated;
updated.setSecsSinceEpoch(entryQuery.value(QStringLiteral("updated")).toInt());
Entry *entry = new Entry(entryQuery.value(QStringLiteral("title")).toString(), entryQuery.value(QStringLiteral("content")).toString(), authors, created, updated, entryQuery.value(QStringLiteral("link")).toString(), nullptr);
m_entries[index] = entry;
}
Feed *EntryListModel::feed() const
{
return m_feed;
}
void EntryListModel::fetch()
void EntryListModel::setFeed(Feed *feed)
{
Fetcher::instance().fetch(QUrl(m_feed));
}
QString EntryListModel::baseUrl(QString url)
{
return QUrl(url).adjusted(QUrl::RemovePath).toString();
m_feed = feed;
emit feedChanged(feed);
}

View File

@ -20,41 +20,35 @@
#pragma once
#include <QAbstractListModel>
#include <QHash>
#include <QObject>
#include <QSqlTableModel>
#include <QString>
class EntryListModel : public QSqlTableModel
#include "entry.h"
class EntryListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QString feed READ feed WRITE setFeed NOTIFY feedChanged)
Q_PROPERTY(Feed *feed READ feed WRITE setFeed NOTIFY feedChanged)
public:
enum DataRole {
Feed = 0,
Id,
Title,
Content,
Created,
Updated,
Link,
Authors,
Enclosure,
};
explicit EntryListModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent) const override;
Q_INVOKABLE void fetch();
Q_INVOKABLE QString baseUrl(QString url);
Feed *feed() const;
QString feed() const;
void setFeed(QString feed);
void setFeed(Feed *feed);
Q_SIGNALS:
void feedChanged(QString feed);
void feedChanged(Feed *feed);
private:
QString m_feed;
void loadEntry(int index) const;
QString enclosure(QString id) const;
Feed *m_feed;
mutable QHash<int, Entry *> m_entries;
};

84
src/feed.cpp Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <QVariant>
#include "database.h"
#include "feed.h"
Feed::Feed(QString url, QString name, QString image, QObject *parent)
: QObject(parent)
, m_url(url)
, m_name(name)
, m_image(image)
{
}
Feed::~Feed()
{
}
QString Feed::url() const
{
return m_url;
}
QString Feed::name() const
{
return m_name;
}
QString Feed::image() const
{
return m_image;
}
void Feed::setName(QString name)
{
m_name = name;
emit nameChanged(m_name);
}
void Feed::setImage(QString image)
{
m_image = image;
emit imageChanged(m_image);
}
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);
}

58
src/feed.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright 2020 Tobias Fella <fella@posteo.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef FEED_H
#define FEED_H
#include <QObject>
class Feed : public QObject
{
Q_OBJECT
Q_PROPERTY(QString url READ url CONSTANT)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString image READ image WRITE setImage NOTIFY imageChanged)
public:
Feed(QString url, QString name, QString image, QObject *parent = nullptr);
~Feed();
QString url() const;
QString name() const;
QString image() const;
void setName(QString name);
void setImage(QString image);
void remove();
Q_SIGNALS:
void nameChanged(QString &name);
void imageChanged(QString &image);
private:
QString m_url;
QString m_name;
QString m_image;
};
#endif // FEED_H

View File

@ -20,62 +20,78 @@
#include <QDebug>
#include <QModelIndex>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QUrl>
#include <QVariant>
#include "database.h"
#include "feedListModel.h"
#include "fetcher.h"
FeedListModel::FeedListModel(QObject *parent)
: QSqlTableModel(parent)
: QAbstractListModel(parent)
{
setTable(QStringLiteral("Feeds"));
setSort(0, Qt::AscendingOrder);
setEditStrategy(OnFieldChange);
select();
connect(&Fetcher::instance(), &Fetcher::updated, this, [this]() { select(); });
connect(&Database::instance(), &Database::feedAdded, this, [this]() {
beginInsertRows(QModelIndex(), rowCount(QModelIndex()) - 1, rowCount(QModelIndex()) - 1);
endInsertRows();
});
connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, [this](QString url, QString name, QString image) {
for (int i = rowCount(QModelIndex()) - 1; i >= 0; i--) {
if (m_feeds[i]->url() == url) {
m_feeds[i]->setName(name);
m_feeds[i]->setImage(image);
Q_EMIT dataChanged(createIndex(i, 0), createIndex(i, 0));
break;
}
}
});
}
QHash<int, QByteArray> FeedListModel::roleNames() const
{
QHash<int, QByteArray> roleNames;
roleNames[Name] = "name";
roleNames[Url] = "url";
roleNames[Image] = "image";
roleNames[0] = "feed";
return roleNames;
}
void FeedListModel::addFeed(QString url)
int FeedListModel::rowCount(const QModelIndex &parent) const
{
Database::instance().addFeed(url);
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();
}
QVariant FeedListModel::data(const QModelIndex &index, int role) const
{
return QSqlTableModel::data(createIndex(index.row(), role), 0);
if (role != 0)
return QVariant();
if (m_feeds[index.row()] == nullptr)
loadFeed(index.row());
return QVariant::fromValue(m_feeds[index.row()]);
}
void FeedListModel::loadFeed(int index) const
{
QSqlQuery query;
query.prepare(QStringLiteral("SELECT * FROM Feeds LIMIT 1 OFFSET :index;"));
query.bindValue(QStringLiteral(":index"), index);
Database::instance().execute(query);
if (!query.next())
qWarning() << "Failed to lod feed" << index;
Feed *feed = new Feed(query.value(QStringLiteral("url")).toString(), query.value(QStringLiteral("name")).toString(), query.value(QStringLiteral("image")).toString(), nullptr);
m_feeds[index] = feed;
}
void FeedListModel::removeFeed(int index)
{
Fetcher::instance().removeImage(data(createIndex(index, 0), Image).toString());
QSqlQuery query;
query.prepare(QStringLiteral("DELETE FROM Authors WHERE feed=:feed;"));
query.bindValue(QStringLiteral(":feed"), data(createIndex(index, 0), 1).toString());
Database::instance().execute(query);
query.prepare(QStringLiteral("DELETE FROM Entries WHERE feed=:feed;"));
query.bindValue(QStringLiteral(":feed"), data(createIndex(index, 0), 1).toString());
Database::instance().execute(query);
query.prepare(QStringLiteral("DELETE FROM Enclosures WHERE feed=:feed;"));
query.bindValue(QStringLiteral(":feed"), data(createIndex(index, 0), 1).toString());
Database::instance().execute(query);
// Workaround...
query.prepare(QStringLiteral("DELETE FROM Feeds WHERE url=:url;"));
query.bindValue(QStringLiteral(":url"), data(createIndex(index, 0), 1).toString());
Database::instance().execute(query);
select();
Feed *feed = m_feeds[index];
beginRemoveRows(QModelIndex(), index, index);
m_feeds[index] = nullptr;
endRemoveRows();
feed->remove();
delete feed;
}

View File

@ -20,27 +20,26 @@
#pragma once
#include <QAbstractListModel>
#include <QHash>
#include <QSqlTableModel>
#include <QUrl>
#include <QVector>
//#include "feed.h"
#include "fetcher.h"
#include "feed.h"
class FeedListModel : public QSqlTableModel
class FeedListModel : public QAbstractListModel
{
Q_OBJECT
public:
enum DataRole {
Name = 0,
Url,
Image,
};
explicit FeedListModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void addFeed(QString url);
int rowCount(const QModelIndex &parent) const override;
Q_INVOKABLE void removeFeed(int index);
private:
void loadFeed(int index) const;
mutable QHash<int, Feed *> m_feeds;
};

View File

@ -38,27 +38,24 @@ Fetcher::Fetcher()
manager->enableStrictTransportSecurityStore(true);
}
void Fetcher::fetch(QUrl url)
void Fetcher::fetch(QString url)
{
qDebug() << "Starting to fetch" << url.toString();
qDebug() << "Starting to fetch" << url;
emit updated();
QNetworkRequest request(url);
QNetworkRequest request((QUrl(url)));
QNetworkReply *reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, [this, url, reply]() {
QByteArray data = reply->readAll();
Syndication::DocumentSource *document = new Syndication::DocumentSource(data, url.toString());
Syndication::DocumentSource *document = new Syndication::DocumentSource(data, url);
Syndication::FeedPtr feed = Syndication::parserCollection()->parse(*document, QStringLiteral("Atom"));
processFeed(feed, url);
emit updated();
delete reply;
});
}
void Fetcher::processFeed(Syndication::FeedPtr feed, QUrl url)
void Fetcher::processFeed(Syndication::FeedPtr feed, QString url)
{
if (feed.isNull())
return;
@ -67,20 +64,25 @@ void Fetcher::processFeed(Syndication::FeedPtr feed, QUrl url)
query.prepare(QStringLiteral("UPDATE Feeds SET name=:name, image=:image WHERE url=:url;"));
query.bindValue(QStringLiteral(":name"), feed->title());
query.bindValue(QStringLiteral(":url"), url);
if (feed->image()->url().startsWith(QStringLiteral("/"))) {
QString absolute = url.adjusted(QUrl::RemovePath).toString() + feed->image()->url();
query.bindValue(QStringLiteral(":image"), absolute);
} else
query.bindValue(QStringLiteral(":image"), feed->image()->url());
QString image;
if (feed->image()->url().startsWith(QStringLiteral("/")))
image = QUrl(url).adjusted(QUrl::RemovePath).toString() + feed->image()->url();
else
image = feed->image()->url();
query.bindValue(QStringLiteral(":image"), image);
Database::instance().execute(query);
qDebug() << "Updated feed title:" << feed->title();
Q_EMIT feedDetailsUpdated(url, feed->title(), image);
for (const auto &entry : feed->items()) {
processEntry(entry, url);
}
Q_EMIT feedUpdated(url);
}
void Fetcher::processEntry(Syndication::ItemPtr entry, QUrl url)
void Fetcher::processEntry(Syndication::ItemPtr entry, QString url)
{
qDebug() << "Processing" << entry->title();
QSqlQuery query;
@ -116,11 +118,11 @@ void Fetcher::processEntry(Syndication::ItemPtr entry, QUrl url)
}
}
void Fetcher::processAuthor(Syndication::PersonPtr author, Syndication::ItemPtr entry, QUrl url)
void Fetcher::processAuthor(Syndication::PersonPtr author, Syndication::ItemPtr entry, QString url)
{
QSqlQuery query;
query.prepare(QStringLiteral("INSERT INTO Authors VALUES(:feed, :id, :name, :uri, :email);"));
query.bindValue(QStringLiteral(":feed"), url.toString());
query.bindValue(QStringLiteral(":feed"), url);
query.bindValue(QStringLiteral(":id"), entry->id());
query.bindValue(QStringLiteral(":name"), author->name());
query.bindValue(QStringLiteral(":uri"), author->uri());
@ -128,11 +130,11 @@ void Fetcher::processAuthor(Syndication::PersonPtr author, Syndication::ItemPtr
Database::instance().execute(query);
}
void Fetcher::processEnclosure(Syndication::EnclosurePtr enclosure, Syndication::ItemPtr entry, QUrl feedUrl)
void Fetcher::processEnclosure(Syndication::EnclosurePtr enclosure, Syndication::ItemPtr entry, QString feedUrl)
{
QSqlQuery query;
query.prepare(QStringLiteral("INSERT INTO Enclosures VALUES (:feed, :id, :duration, :size, :title, :type, :url);"));
query.bindValue(QStringLiteral(":feed"), feedUrl.toString());
query.bindValue(QStringLiteral(":feed"), feedUrl);
query.bindValue(QStringLiteral(":id"), entry->id());
query.bindValue(QStringLiteral(":duration"), enclosure->duration());
query.bindValue(QStringLiteral(":size"), enclosure->length());
@ -165,7 +167,6 @@ void Fetcher::download(QString url)
file.write(data);
file.close();
emit updated();
delete reply;
});
}

View File

@ -34,23 +34,23 @@ public:
static Fetcher _instance;
return _instance;
}
void fetch(QUrl);
Q_INVOKABLE void fetch(QString url);
Q_INVOKABLE QString image(QString);
void removeImage(QString);
Q_INVOKABLE void download(QString url);
private:
Fetcher();
Fetcher(const Fetcher &);
QString filePath(QString);
void processFeed(Syndication::FeedPtr feed, QUrl url);
void processEntry(Syndication::ItemPtr entry, QUrl url);
void processAuthor(Syndication::PersonPtr author, Syndication::ItemPtr entry, QUrl url);
void processEnclosure(Syndication::EnclosurePtr enclosure, Syndication::ItemPtr entry, QUrl feedUrl);
void processFeed(Syndication::FeedPtr feed, QString url);
void processEntry(Syndication::ItemPtr entry, QString url);
void processAuthor(Syndication::PersonPtr author, Syndication::ItemPtr entry, QString url);
void processEnclosure(Syndication::EnclosurePtr enclosure, Syndication::ItemPtr entry, QString feedUrl);
QNetworkAccessManager *manager;
Q_SIGNALS:
void updated();
void feedUpdated(QString url);
void feedDetailsUpdated(QString url, QString name, QString image);
};

View File

@ -61,6 +61,10 @@ int main(int argc, char *argv[])
engine->setObjectOwnership(&Fetcher::instance(), QQmlEngine::CppOwnership);
return &Fetcher::instance();
});
qmlRegisterSingletonType<Database>("org.kde.alligator", 1, 0, "Database", [](QQmlEngine *engine, QJSEngine *) -> QObject * {
engine->setObjectOwnership(&Database::instance(), QQmlEngine::CppOwnership);
return &Database::instance();
});
QQmlApplicationEngine engine;
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));

View File

@ -29,13 +29,11 @@ import org.kde.alligator 1.0
Kirigami.ScrollablePage {
id: page
property var name
property var url
property var image
property var feed
title: name
title: feed.title
property var all: page.url === "all"
//property bool all: feed
contextualActions: [
Kirigami.Action {
@ -48,11 +46,7 @@ Kirigami.ScrollablePage {
actions.main: Kirigami.Action {
iconName: "view-refresh"
text: i18n("Refresh Feed")
onTriggered: entryListModel.fetch()
}
Component.onCompleted: {
entryListModel.fetch();
onTriggered: Fetcher.fetch(page.feed.url)
}
Kirigami.PlaceholderMessage {
@ -69,7 +63,7 @@ Kirigami.ScrollablePage {
visible: count !== 0
model: EntryListModel {
id: entryListModel
feed: page.url
feed: page.feed
}
header: RowLayout {
@ -77,13 +71,12 @@ Kirigami.ScrollablePage {
height: root.height * 0.2
visible: !all
Kirigami.Icon {
source: Fetcher.image(page.image)
source: Fetcher.image(page.feed.image)
width: height
height: parent.height
Component.onCompleted: console.log("Height: " + page.height)
}
Kirigami.Heading {
text: page.name
text: page.feed.name
}
}
@ -93,14 +86,14 @@ Kirigami.ScrollablePage {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Controls.Label {
text: model.title
text: model.entry.title
Layout.fillWidth: true
elide: Text.ElideRight
opacity: 1
}
Controls.Label {
id: subtitleItem
text: model.updated.toLocaleString(Qt.locale(), Locale.ShortFormat) + (model.authors.length === 0 ? "" : " " + i18nc("by <author(s)>", "by") + " " + model.authors.join(", "))
text: model.entry.updated.toLocaleString(Qt.locale(), Locale.ShortFormat) + (model.entry.authors.length === 0 ? "" : " " + i18nc("by <author(s)>", "by") + " " + model.entry.authors[0].name)
Layout.fillWidth: true
elide: Text.ElideRight
font: Kirigami.Theme.smallFont
@ -110,8 +103,7 @@ Kirigami.ScrollablePage {
}
onClicked: {
model.read = true;
pageStack.push("qrc:/EntryPage.qml", {"data": model, "baseUrl": entryListModel.baseUrl(model.link)})
pageStack.push("qrc:/EntryPage.qml", {"entry": model.entry})
}
}
}

View File

@ -28,15 +28,16 @@ import org.kde.alligator 1.0
Kirigami.ScrollablePage {
id: page
property QtObject data
property alias baseUrl: label.baseUrl
title: data.title
property QtObject entry
title: entry.title
ColumnLayout {
Controls.Label {
id: label
text: page.data.content
baseUrl: page.entry.baseUrl
text: page.entry.content
textFormat: Text.RichText
wrapMode: Text.WordWrap
Layout.fillWidth: true

View File

@ -58,7 +58,7 @@ Kirigami.ScrollablePage {
text: i18n("Add Feed")
enabled: urlField.text
onClicked: {
feedListModel.addFeed(urlField.text)
Database.addFeed(urlField.text)
addSheet.close()
}
}
@ -82,6 +82,7 @@ Kirigami.ScrollablePage {
id: feedListModel
}
/*
header:
Kirigami.AbstractListItem {
Controls.Label {
@ -95,6 +96,7 @@ Kirigami.ScrollablePage {
pageStack.push("qrc:/EntryListPage.qml")
}
}
*/
delegate: Kirigami.SwipeListItem {
height: Kirigami.Units.gridUnit*2
@ -102,13 +104,13 @@ Kirigami.ScrollablePage {
Item {
Kirigami.Icon {
id: icon
source: Fetcher.image(model.image)
source: Fetcher.image(model.feed.image)
width: height
height: parent.height
}
Controls.Label {
text: model.name
text: model.feed.name
height: parent.height
anchors.left: icon.right
leftPadding: 0.5*Kirigami.Units.gridUnit
@ -119,7 +121,7 @@ Kirigami.ScrollablePage {
Kirigami.Action {
icon.name: "delete"
onTriggered: {
if(pageStack.depth > 1 && model.url === lastFeed)
if(pageStack.depth > 1 && model.feed.url === lastFeed)
pageStack.pop()
feedListModel.removeFeed(index)
}
@ -128,8 +130,8 @@ Kirigami.ScrollablePage {
]
onClicked: {
lastFeed = model.url
pageStack.push("qrc:/EntryListPage.qml", {"name": name, "url": url, "image": image})
lastFeed = model.feed.url
pageStack.push("qrc:/EntryListPage.qml", {"feed": model.feed})
}
}
}