From a5a449c08b82a44b19bf441bf95f2bfbfed7c019 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Sat, 6 Jun 2020 00:05:32 +0200 Subject: [PATCH] Implement FeedDetailsPage --- src/database.cpp | 15 ++-- src/entry.cpp | 31 ++++++++- src/entry.h | 14 +++- src/entryListModel.cpp | 11 ++- src/feed.cpp | 125 ++++++++++++++++++++++++++++++++-- src/feed.h | 59 +++++++++++++++- src/feedListModel.cpp | 26 ++++++- src/fetcher.cpp | 14 ++-- src/fetcher.h | 2 +- src/qml/EntryListDelegate.qml | 3 + src/qml/EntryListPage.qml | 2 +- src/qml/FeedDetailsPage.qml | 39 ++++++++++- 12 files changed, 317 insertions(+), 24 deletions(-) diff --git a/src/database.cpp b/src/database.cpp index 3cef5241..12fd1740 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -57,8 +57,8 @@ bool Database::migrate() bool Database::migrateTo1() { qDebug() << "Migrating database to version 1"; - TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Feeds (name TEXT, url TEXT, image TEXT, link TEXT, description TEXT);"))); - TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Entries (feed TEXT, id TEXT UNIQUE, title TEXT, content TEXT, created INTEGER, updated INTEGER, link TEXT);"))); + TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Feeds (name TEXT, url TEXT, image TEXT, link TEXT, description TEXT, deleteAfterCount INTEGER, deleteAfterType INTEGER, subscribed INTEGER, lastUpdated INTEGER, autoUpdateCount INTEGER, autoUpdateType INTEGER, notify BOOL);"))); + TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Entries (feed TEXT, id TEXT UNIQUE, title TEXT, content TEXT, created INTEGER, updated INTEGER, link TEXT, read bool);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Authors (feed TEXT, id TEXT, name TEXT, uri TEXT, email TEXT);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Enclosures (feed TEXT, id TEXT, duration INTEGER, size INTEGER, title TEXT, type STRING, url STRING);"))); TRUE_OR_RETURN(execute(QStringLiteral("PRAGMA user_version = 1;"))); @@ -106,7 +106,7 @@ void Database::cleanup() int count = settings.deleteAfterCount(); int type = settings.deleteAfterType(); - if(type == 0) { //Never delete Entries + if (type == 0) { // Never delete Entries return; } @@ -149,12 +149,19 @@ void Database::addFeed(QString url) qDebug() << "Feed does not yet exist"; QSqlQuery query; - query.prepare(QStringLiteral("INSERT INTO Feeds VALUES (:name, :url, :image, :link, :description);")); + query.prepare(QStringLiteral("INSERT INTO Feeds VALUES (:name, :url, :image, :link, :description, :deleteAfterCount, :deleteAfterType, :subscribed, :lastUpdated, :autoUpdateCount, :autoUpdateType, :notify);")); query.bindValue(QStringLiteral(":name"), url); query.bindValue(QStringLiteral(":url"), url); 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(":autoUpdateCount"), 0); + query.bindValue(QStringLiteral(":autoUpdateType"), 0); + query.bindValue(QStringLiteral(":notify"), false); execute(query); Q_EMIT feedAdded(url); diff --git a/src/entry.cpp b/src/entry.cpp index 4020ebb4..a45281ef 100644 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -19,16 +19,23 @@ */ #include "entry.h" + +#include #include -Entry::Entry(QString title, QString content, QVector authors, QDateTime created, QDateTime updated, QString link, QObject *parent) +#include "database.h" + +Entry::Entry(Feed *feed, QString id, QString title, QString content, QVector authors, QDateTime created, QDateTime updated, QString link, bool read, QObject *parent) : QObject(parent) + , m_feed(feed) + , m_id(id) , m_title(title) , m_content(content) , m_authors(authors) , m_created(created) , m_updated(updated) , m_link(link) + , m_read(read) { } @@ -36,6 +43,11 @@ Entry::~Entry() { } +QString Entry::id() const +{ + return m_id; +} + QString Entry::title() const { return m_title; @@ -66,7 +78,24 @@ QString Entry::link() const return m_link; } +bool Entry::read() const +{ + return m_read; +} + QString Entry::baseUrl() const { return QUrl(m_link).adjusted(QUrl::RemovePath).toString(); } + +void Entry::setRead(bool read) +{ + m_read = read; + Q_EMIT readChanged(m_read); + QSqlQuery query; + query.prepare(QStringLiteral("UPDATE Entries SET read=:read WHERE id=:id AND feed=:feed")); + query.bindValue(QStringLiteral(":id"), m_id); + query.bindValue(QStringLiteral(":feed"), m_feed->url()); + query.bindValue(QStringLiteral(":read"), m_read); + Database::instance().execute(query); +} diff --git a/src/entry.h b/src/entry.h index 41b27910..3c9f6160 100644 --- a/src/entry.h +++ b/src/entry.h @@ -34,6 +34,7 @@ class Entry : public QObject { Q_OBJECT + Q_PROPERTY(QString id READ id CONSTANT) Q_PROPERTY(QString title READ title CONSTANT) Q_PROPERTY(QString content READ content CONSTANT) Q_PROPERTY(QVector authors READ authors CONSTANT) @@ -41,27 +42,38 @@ class Entry : public QObject Q_PROPERTY(QDateTime updated READ updated CONSTANT) Q_PROPERTY(QString link READ link CONSTANT) Q_PROPERTY(QString baseUrl READ baseUrl CONSTANT) + Q_PROPERTY(bool read READ read WRITE setRead NOTIFY readChanged); public: - Entry(QString title, QString content, QVector authors, QDateTime created, QDateTime updated, QString link, QObject *parent = nullptr); + Entry(Feed *feed, QString id, QString title, QString content, QVector authors, QDateTime created, QDateTime updated, QString link, bool read, QObject *parent = nullptr); ~Entry(); + QString id() const; QString title() const; QString content() const; QVector authors() const; QDateTime created() const; QDateTime updated() const; QString link() const; + bool read() const; + QString baseUrl() const; + void setRead(bool read); + +Q_SIGNALS: + void readChanged(bool read); + private: Feed *m_feed; + QString m_id; QString m_title; QString m_content; QVector m_authors; QDateTime m_created; QDateTime m_updated; QString m_link; + bool m_read; }; #endif // ENTRY_H diff --git a/src/entryListModel.cpp b/src/entryListModel.cpp index 423f7350..2d84f472 100644 --- a/src/entryListModel.cpp +++ b/src/entryListModel.cpp @@ -94,7 +94,16 @@ void EntryListModel::loadEntry(int index) const 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); + Entry *entry = new Entry(m_feed, + entryQuery.value(QStringLiteral("id")).toString(), + entryQuery.value(QStringLiteral("title")).toString(), + entryQuery.value(QStringLiteral("content")).toString(), + authors, + created, + updated, + entryQuery.value(QStringLiteral("link")).toString(), + entryQuery.value(QStringLiteral("read")).toBool(), + nullptr); m_entries[index] = entry; } diff --git a/src/feed.cpp b/src/feed.cpp index 8ebeb03e..31afcf93 100644 --- a/src/feed.cpp +++ b/src/feed.cpp @@ -24,7 +24,20 @@ #include "feed.h" #include "fetcher.h" -Feed::Feed(QString url, QString name, QString image, QString link, QString description, QVector authors, QObject *parent) +Feed::Feed(QString url, + QString name, + QString image, + QString link, + QString description, + QVector authors, + int deleteAfterCount, + int deleteAfterType, + QDateTime subscribed, + QDateTime lastUpdated, + int autoUpdateCount, + int autoUpdateType, + bool notify, + QObject *parent) : QObject(parent) , m_url(url) , m_name(name) @@ -32,15 +45,24 @@ Feed::Feed(QString url, QString name, QString image, QString link, QString descr , m_link(link) , m_description(description) , m_authors(authors) + , m_deleteAfterCount(deleteAfterCount) + , m_deleteAfterType(deleteAfterType) + , m_subscribed(subscribed) + , m_lastUpdated(lastUpdated) + , m_autoUpdateCount(autoUpdateCount) + , m_autoUpdateType(autoUpdateType) + , m_notify(notify) { - connect(&Fetcher::instance(), &Fetcher::startedFetchingFeed, this, [this] (QString url) { - if(url == m_url) { + connect(&Fetcher::instance(), &Fetcher::startedFetchingFeed, this, [this](QString url) { + if (url == m_url) { setRefreshing(true); } }); - connect(&Fetcher::instance(), &Fetcher::feedUpdated, this, [this] (QString url) { - if(url == m_url) { + connect(&Fetcher::instance(), &Fetcher::feedUpdated, this, [this](QString url) { + if (url == m_url) { setRefreshing(false); + emit entryCountChanged(); + emit unreadEntryCountChanged(); } }); } @@ -79,6 +101,63 @@ QVector Feed::authors() const return m_authors; } +int Feed::deleteAfterCount() const +{ + return m_deleteAfterCount; +} + +int Feed::deleteAfterType() const +{ + return m_deleteAfterType; +} + +QDateTime Feed::subscribed() const +{ + return m_subscribed; +} + +QDateTime Feed::lastUpdated() const +{ + return m_lastUpdated; +} + +int Feed::autoUpdateCount() const +{ + return m_autoUpdateCount; +} + +int Feed::autoUpdateType() const +{ + return m_autoUpdateType; +} + +bool Feed::notify() const +{ + return m_notify; +} + +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(); +} + +int Feed::unreadEntryCount() const +{ + QSqlQuery query; + query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND read=false;")); + query.bindValue(QStringLiteral(":feed"), m_url); + Database::instance().execute(query); + if (!query.next()) + return -1; + return query.value(0).toInt(); +} + bool Feed::refreshing() const { return m_refreshing; @@ -114,6 +193,42 @@ void Feed::setAuthors(QVector authors) Q_EMIT authorsChanged(m_authors); } +void Feed::setDeleteAfterCount(int count) +{ + m_deleteAfterCount = count; + Q_EMIT deleteAfterCountChanged(m_deleteAfterCount); +} + +void Feed::setDeleteAfterType(int type) +{ + m_deleteAfterType = type; + Q_EMIT deleteAfterTypeChanged(m_deleteAfterType); +} + +void Feed::setLastUpdated(QDateTime lastUpdated) +{ + m_lastUpdated = lastUpdated; + Q_EMIT lastUpdatedChanged(m_lastUpdated); +} + +void Feed::setAutoUpdateCount(int count) +{ + m_autoUpdateCount = count; + Q_EMIT autoUpdateCountChanged(m_autoUpdateCount); +} + +void Feed::setAutoUpdateType(int type) +{ + m_autoUpdateType = type; + Q_EMIT autoUpdateTypeChanged(m_autoUpdateType); +} + +void Feed::setNotify(bool notify) +{ + m_notify = notify; + Q_EMIT notifyChanged(m_notify); +} + void Feed::setRefreshing(bool refreshing) { m_refreshing = refreshing; diff --git a/src/feed.h b/src/feed.h index 6afca280..e9e27409 100644 --- a/src/feed.h +++ b/src/feed.h @@ -21,6 +21,7 @@ #ifndef FEED_H #define FEED_H +#include #include #include "author.h" @@ -36,9 +37,31 @@ class Feed : public QObject Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) Q_PROPERTY(QVector authors READ authors WRITE setAuthors NOTIFY authorsChanged) Q_PROPERTY(bool refreshing READ refreshing WRITE setRefreshing NOTIFY refreshingChanged) + Q_PROPERTY(int deleteAfterCount READ deleteAfterCount WRITE setDeleteAfterCount NOTIFY deleteAfterCountChanged) + Q_PROPERTY(int deleteAfterType READ deleteAfterType WRITE setDeleteAfterType NOTIFY deleteAfterTypeChanged) + Q_PROPERTY(QDateTime subscribed READ subscribed CONSTANT) + Q_PROPERTY(QDateTime lastUpdated READ lastUpdated WRITE setLastUpdated NOTIFY lastUpdatedChanged) + Q_PROPERTY(int autoUpdateCount READ autoUpdateCount WRITE setAutoUpdateCount NOTIFY autoUpdateCountChanged) + Q_PROPERTY(int autoUpdateType READ autoUpdateType WRITE setAutoUpdateType NOTIFY autoUpdateCountChanged) + Q_PROPERTY(bool notify READ notify WRITE setNotify NOTIFY notifyChanged) + Q_PROPERTY(int entryCount READ entryCount NOTIFY entryCountChanged) + Q_PROPERTY(int unreadEntryCount READ unreadEntryCount NOTIFY unreadEntryCountChanged) public: - Feed(QString url, QString name, QString image, QString link, QString description, QVector authors, QObject *parent = nullptr); + Feed(QString url, + QString name, + QString image, + QString link, + QString description, + QVector authors, + int deleteAfterCount, + int deleteAfterType, + QDateTime subscribed, + QDateTime lastUpdated, + int autoUpdateCount, + int autoUpdateType, + bool notify, + QObject *parent = nullptr); ~Feed(); @@ -48,6 +71,17 @@ public: QString link() const; QString description() const; QVector authors() const; + int deleteAfterCount() const; + int deleteAfterType() const; + QDateTime subscribed() const; + QDateTime lastUpdated() const; + int autoUpdateCount() const; + int autoUpdateType() const; + bool notify() const; + int entryCount() const; + int unreadEntryCount() const; + bool read() const; + bool refreshing() const; void setName(QString name); @@ -55,6 +89,12 @@ public: void setLink(QString link); void setDescription(QString description); void setAuthors(QVector authors); + void setDeleteAfterCount(int count); + void setDeleteAfterType(int type); + void setLastUpdated(QDateTime lastUpdated); + void setAutoUpdateCount(int count); + void setAutoUpdateType(int type); + void setNotify(bool notify); void setRefreshing(bool refreshing); Q_INVOKABLE void refresh(); @@ -66,6 +106,15 @@ Q_SIGNALS: void linkChanged(QString &link); void descriptionChanged(QString &description); void authorsChanged(QVector &authors); + void deleteAfterCountChanged(int count); + void deleteAfterTypeChanged(int type); + void lastUpdatedChanged(QDateTime lastUpdated); + void autoUpdateCountChanged(int count); + void autoUpdateTypeChanged(int type); + void notifyChanged(bool notify); + void entryCountChanged(); + void unreadEntryCountChanged(); + void refreshingChanged(bool refreshing); private: @@ -75,6 +124,14 @@ private: QString m_link; QString m_description; QVector m_authors; + int m_deleteAfterCount; + int m_deleteAfterType; + QDateTime m_subscribed; + QDateTime m_lastUpdated; + int m_autoUpdateCount; + int m_autoUpdateType; + bool m_notify; + bool m_refreshing = false; }; diff --git a/src/feedListModel.cpp b/src/feedListModel.cpp index ec508ba0..60d63800 100644 --- a/src/feedListModel.cpp +++ b/src/feedListModel.cpp @@ -35,13 +35,14 @@ FeedListModel::FeedListModel(QObject *parent) beginInsertRows(QModelIndex(), rowCount(QModelIndex()) - 1, rowCount(QModelIndex()) - 1); endInsertRows(); }); - connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, [this](QString url, QString name, QString image, QString link, QString description) { + connect(&Fetcher::instance(), &Fetcher::feedDetailsUpdated, this, [this](QString url, QString name, QString image, QString link, QString description, QDateTime lastUpdated) { 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); 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; } @@ -94,7 +95,26 @@ void FeedListModel::loadFeed(int index) const authors += new Author(authorQuery.value(QStringLiteral("name")).toString(), authorQuery.value(QStringLiteral("email")).toString(), authorQuery.value(QStringLiteral("uri")).toString(), nullptr); } - Feed *feed = new Feed(query.value(QStringLiteral("url")).toString(), query.value(QStringLiteral("name")).toString(), query.value(QStringLiteral("image")).toString(), query.value(QStringLiteral("link")).toString(), query.value(QStringLiteral("description")).toString(), authors, nullptr); + QDateTime subscribed; + subscribed.setSecsSinceEpoch(query.value(QStringLiteral("subscribed")).toInt()); + + QDateTime lastUpdated; + lastUpdated.setSecsSinceEpoch(query.value(QStringLiteral("lastUpdated")).toInt()); + + Feed *feed = new Feed(query.value(QStringLiteral("url")).toString(), + query.value(QStringLiteral("name")).toString(), + query.value(QStringLiteral("image")).toString(), + query.value(QStringLiteral("link")).toString(), + query.value(QStringLiteral("description")).toString(), + authors, + query.value(QStringLiteral("deleteAfterCount")).toInt(), + query.value(QStringLiteral("deleteAfterType")).toInt(), + subscribed, + lastUpdated, + query.value(QStringLiteral("autoUpdateCount")).toInt(), + query.value(QStringLiteral("autoUpdateType")).toInt(), + query.value(QStringLiteral("notify")).toBool(), + nullptr); m_feeds[index] = feed; } @@ -110,7 +130,7 @@ void FeedListModel::removeFeed(int index) void FeedListModel::refreshAll() { - for(auto &feed : m_feeds) { + for (auto &feed : m_feeds) { feed->refresh(); } } \ No newline at end of file diff --git a/src/fetcher.cpp b/src/fetcher.cpp index 883734d5..ac860dbe 100644 --- a/src/fetcher.cpp +++ b/src/fetcher.cpp @@ -18,6 +18,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -62,7 +63,7 @@ void Fetcher::fetchAll() QSqlQuery query; query.prepare(QStringLiteral("SELECT url FROM Feeds;")); Database::instance().execute(query); - while(query.next()) { + while (query.next()) { fetch(query.value(0).toString()); } } @@ -73,13 +74,16 @@ void Fetcher::processFeed(Syndication::FeedPtr feed, QString url) return; QSqlQuery query; - query.prepare(QStringLiteral("UPDATE Feeds SET name=:name, image=:image, link=:link, description=:description WHERE url=:url;")); + query.prepare(QStringLiteral("UPDATE Feeds SET name=:name, image=:image, link=:link, description=:description, lastUpdated=:lastUpdated WHERE url=:url;")); query.bindValue(QStringLiteral(":name"), feed->title()); query.bindValue(QStringLiteral(":url"), url); query.bindValue(QStringLiteral(":link"), feed->link()); query.bindValue(QStringLiteral(":description"), feed->description()); - for(auto &author : feed->authors()) { + QDateTime current = QDateTime::currentDateTime(); + query.bindValue(QStringLiteral(":lastUpdated"), current.toSecsSinceEpoch()); + + for (auto &author : feed->authors()) { processAuthor(author, QLatin1String(""), url); } @@ -93,7 +97,7 @@ void Fetcher::processFeed(Syndication::FeedPtr feed, QString url) qDebug() << "Updated feed title:" << feed->title(); - Q_EMIT feedDetailsUpdated(url, feed->title(), image, feed->link(), feed->description()); + Q_EMIT feedDetailsUpdated(url, feed->title(), image, feed->link(), feed->description(), current); for (const auto &entry : feed->items()) { processEntry(entry, url); @@ -114,7 +118,7 @@ void Fetcher::processEntry(Syndication::ItemPtr entry, QString url) if (query.value(0).toInt() != 0) return; - query.prepare(QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :content, :created, :updated, :link);")); + query.prepare(QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :content, :created, :updated, :link, false);")); query.bindValue(QStringLiteral(":feed"), url); query.bindValue(QStringLiteral(":id"), entry->id()); query.bindValue(QStringLiteral(":title"), QTextDocumentFragment::fromHtml(entry->title()).toPlainText()); diff --git a/src/fetcher.h b/src/fetcher.h index bb696278..a442131f 100644 --- a/src/fetcher.h +++ b/src/fetcher.h @@ -54,5 +54,5 @@ private: Q_SIGNALS: void startedFetchingFeed(QString url); void feedUpdated(QString url); - void feedDetailsUpdated(QString url, QString name, QString image, QString link, QString description); + void feedDetailsUpdated(QString url, QString name, QString image, QString link, QString description, QDateTime lastUpdated); }; diff --git a/src/qml/EntryListDelegate.qml b/src/qml/EntryListDelegate.qml index a78d658d..e2acbd15 100644 --- a/src/qml/EntryListDelegate.qml +++ b/src/qml/EntryListDelegate.qml @@ -36,6 +36,7 @@ Kirigami.SwipeListItem { Layout.fillWidth: true elide: Text.ElideRight opacity: 1 + color: model.entry.read ? Kirigami.Theme.disabledTextColor : Kirigami.Theme.textColor } Controls.Label { id: subtitleItem @@ -45,10 +46,12 @@ Kirigami.SwipeListItem { font: Kirigami.Theme.smallFont opacity: 0.6 visible: text.length > 0 + color: model.entry.read ? Kirigami.Theme.disabledTextColor : Kirigami.Theme.textColor } } onClicked: { + model.entry.read = true pageStack.push("qrc:/EntryPage.qml", {"entry": model.entry}) } } \ No newline at end of file diff --git a/src/qml/EntryListPage.qml b/src/qml/EntryListPage.qml index 7337ccba..104054e9 100644 --- a/src/qml/EntryListPage.qml +++ b/src/qml/EntryListPage.qml @@ -44,7 +44,7 @@ Kirigami.ScrollablePage { Kirigami.Action { iconName: "help-about-symbolic" text: i18n("Details") - onTriggered: pageStack.push("qrc:/FeedDetailsPage.qml") + onTriggered: pageStack.push("qrc:/FeedDetailsPage.qml", {"feed": feed}) } ] diff --git a/src/qml/FeedDetailsPage.qml b/src/qml/FeedDetailsPage.qml index e655875b..1ea684c9 100644 --- a/src/qml/FeedDetailsPage.qml +++ b/src/qml/FeedDetailsPage.qml @@ -24,7 +24,44 @@ import QtQuick.Layouts 1.14 import org.kde.kirigami 2.12 as Kirigami +import org.kde.alligator 1.0 -Kirigami.Page { +Kirigami.ScrollablePage { + id: detailsPage + property QtObject feed; + + title: i18nc(" - Details", "%1 - Details", feed.name) + + ColumnLayout { + Kirigami.Icon { + source: Fetcher.image(feed.image) + height: 200 + width: height + } + Kirigami.Heading { + text: feed.name + } + Kirigami.Heading { + text: feed.description; + level: 3 + } + Controls.Label { + text: i18nc("by ", "by %1", feed.authors[0].name) + visible: feed.authors.length !== 0 + } + Controls.Label { + text: "%1".arg(feed.link) + onLinkActivated: Qt.openUrlExternally(link) + } + Controls.Label { + text: i18n("Subscribed since: %1", feed.subscribed.toLocaleString(Qt.locale(), Locale.ShortFormat)) + } + Controls.Label { + text: i18n("last updated: %1", feed.lastUpdated.toLocaleString(Qt.locale(), Locale.ShortFormat)) + } + Controls.Label { + text: i18n("%1 posts, %2 unread", feed.entryCount, feed.unreadEntryCount) + } + } }