Expanded the model with more roles and designed frontend.

This commit is contained in:
Swapnil Tripathi 2021-07-02 03:49:03 +05:30 committed by Bart De Vries
parent fa1c308c2d
commit 95ee97aefe
8 changed files with 250 additions and 5 deletions

View File

@ -141,6 +141,8 @@ if(ANDROID)
overflow-menu
checkbox
error
search
kt-add-feeds
)
else()
target_link_libraries(kasts PRIVATE Qt::Widgets Qt::DBus)

View File

@ -626,3 +626,8 @@ void DataManager::updateQueueListnrs() const
Database::instance().execute(query);
}
}
bool DataManager::isFeedExists(const QString &url)
{
return m_feeds.contains(url);
}

View File

@ -59,6 +59,7 @@ public:
Q_INVOKABLE void importFeeds(const QString &path);
Q_INVOKABLE void exportFeeds(const QString &path);
Q_INVOKABLE bool isFeedExists(const QString &url);
Q_SIGNALS:
void feedAdded(const QString &url);

View File

@ -30,15 +30,95 @@ QVariant PodcastSearchModel::data(const QModelIndex &index, int role) const
// invalid index
return QVariant::fromValue(QStringLiteral("DEADBEEF"));
}
if (role == Title) {
switch (role) {
case Id:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("id")].toInt();
case Title:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("title")].toString();
case Url:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("url")].toString();
case OriginalUrl:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("originalUrl")].toString();
case Link:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("link")].toString();
case Description:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("description")].toString();
case Author:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("author")].toString();
case OwnerName:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("ownerName")].toString();
case Image:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("image")].toString();
case Artwork:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("artwork")].toString();
case LastUpdateTime:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("lastUpdateTime")].toInt();
case LastCrawlTime:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("lastCrawlTime")].toInt();
case LastParseTime:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("lastParseTime")].toInt();
case LastGoodHttpStatusTime:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("lastGoodHttpStatusTime")].toInt();
case LastHttpStatus:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("lastHttpStatus")].toInt();
case ContentType:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("contentType")].toString();
case ItunesId:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("itunesId")].toInt();
case Generator:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("generator")].toString();
case Language:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("language")].toString();
case Type:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("type")].toInt();
case Dead:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("dead")].toInt();
case CrawlErrors:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("crawlErrors")].toInt();
case ParseErrors:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("parseErrors")].toInt();
case Categories: {
// TODO: Implement this function to add to the list of categories.
}
case Locked:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("locked")].toInt();
case ImageUrlHash:
return m_data[QStringLiteral("feeds")].toArray()[index.row()].toObject()[QStringLiteral("imageUrlHash")].toInt();
default:
return QVariant();
}
return QVariant();
}
QHash<int, QByteArray> PodcastSearchModel::roleNames() const
{
return {{Title, "title"}};
return {
{Id, "id"},
{Title, "title"},
{Url, "url"},
{OriginalUrl, "originalUrl"},
{Link, "link"},
{Description, "description"},
{Author, "author"},
{OwnerName, "ownerName"},
{Image, "image"},
{Artwork, "artwork"},
{LastUpdateTime, "lastUpdateTime"},
{LastCrawlTime, "lastCrawlTime"},
{LastParseTime, "lastParseTime"},
{LastGoodHttpStatusTime, "lastGoodHttpStatusTime"},
{LastHttpStatus, "lastHttpStatus"},
{ContentType, "contentType"},
{ItunesId, "itunesId"},
{Generator, "generator"},
{Language, "language"},
{Type, "type"},
{Dead, "dead"},
{CrawlErrors, "crawlErrors"},
{ParseErrors, "parseErrors"},
{Categories, "categories"},
{Locked, "locked"},
{ImageUrlHash, "imageUrlHash"},
};
}
int PodcastSearchModel::rowCount(const QModelIndex &parent) const
@ -47,7 +127,8 @@ int PodcastSearchModel::rowCount(const QModelIndex &parent) const
if (m_data.isEmpty()) {
return 0;
}
return m_data[QStringLiteral("feeds")].toArray().size();
return m_data[QStringLiteral("feeds")].toArray().size();
}
void PodcastSearchModel::search(const QString &text)

View File

@ -17,10 +17,34 @@
class PodcastSearchModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
Id,
Title,
Url,
OriginalUrl,
Link,
Description,
Author,
OwnerName,
Image,
Artwork,
LastUpdateTime,
LastCrawlTime,
LastParseTime,
LastGoodHttpStatusTime,
LastHttpStatus,
ContentType,
ItunesId,
Generator,
Language,
Type,
Dead,
CrawlErrors,
ParseErrors,
Categories,
Locked,
ImageUrlHash,
};
explicit PodcastSearchModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

119
src/qml/DiscoverPage.qml Normal file
View File

@ -0,0 +1,119 @@
/**
* SPDX-FileCopyrightText: 2021 Swapnil Tripathi <swapnil06.st@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick 2.14
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.14
import org.kde.kirigami 2.15 as Kirigami
import org.kde.kasts 1.0
Kirigami.ScrollablePage {
id: page
title: i18n("Discover")
property var feedModel: ""
header: RowLayout {
width: parent.width
anchors.topMargin: Kirigami.Units.smallSpacing
Kirigami.SearchField {
id: textField
placeholderText: i18n("Search podcastindex.org")
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.smallSpacing
}
Controls.Button {
id: searchButton
text: isWidescreen ? i18n("Search") : ""
icon.name: "search"
Layout.rightMargin: Kirigami.Units.smallSpacing
onClicked: {
podcastSearchModel.search(textField.text);
}
}
}
Component.onCompleted: {
textField.forceActiveFocus();
}
Component {
id: delegateComponent
Kirigami.SwipeListItem {
id: listItem
contentItem: RowLayout {
Kirigami.Icon {
source: model.image
Layout.fillHeight: true
Layout.maximumHeight: Kirigami.Units.iconSizes.huge
Layout.preferredWidth: height
}
Controls.Label {
Layout.fillWidth: true
height: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium)
text: model.title
elide: Text.ElideRight
color: listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) ? listItem.activeTextColor : listItem.textColor
}
}
actions: [
Kirigami.Action {
text: enabled ? i18n("Subscribe") : i18n("Subscribed")
icon.name: "kt-add-feeds"
enabled: !DataManager.isFeedExists(model.url)
onTriggered: {
DataManager.addFeed(model.url)
}
}
]
onClicked: {
feedModel = model
detailDrawer.open();
}
}
}
ListView {
anchors.fill: parent
model: PodcastSearchModel {
id: podcastSearchModel
}
spacing: 5
clip: true
delegate: Kirigami.DelegateRecycler {
width: parent ? parent.width : implicitWidth
sourceComponent: delegateComponent
}
}
Kirigami.OverlaySheet {
id: detailDrawer
showCloseButton: true
contentItem: ColumnLayout {
Layout.preferredWidth: Kirigami.Units.gridUnit * 25
GenericHeader {
image: feedModel.image
title: feedModel.title
subtitle: feedModel.author
Controls.Button {
text: enabled ? "Subscribe" : "Subscribed"
icon.name: "kt-add-feeds"
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: Kirigami.Units.largeSpacing
anchors.topMargin: Kirigami.Units.largeSpacing
onClicked: {
DataManager.addFeed(feedModel.url)
}
enabled: !DataManager.isFeedExists(feedModel.url)
}
}
Controls.Label {
text: i18n("%1 \n\nAuthor: %2 \nOwner: %3", feedModel.description, feedModel.author, feedModel.ownerName)
Layout.margins: Kirigami.Units.gridUnit
wrapMode: Text.WordWrap
Layout.fillWidth: true
onLinkActivated: Qt.openUrlExternally(link)
font.pointSize: SettingsManager && !(SettingsManager.articleFontUseSystem) ? SettingsManager.articleFontSize : Kirigami.Units.fontMetrics.font.pointSize
}
}
}
}

View File

@ -38,6 +38,7 @@ Kirigami.ApplicationWindow {
switch (page) {
case "QueuePage": return "qrc:/QueuePage.qml";
case "EpisodeSwipePage": return "qrc:/EpisodeSwipePage.qml";
case "DiscoverPage": return "qrc:/DiscoverPage.qml";
case "FeedListPage": return "qrc:/FeedListPage.qml";
case "DownloadListPage": return "qrc:/DownloadListPage.qml";
case "SettingsPage": return "qrc:/SettingsPage.qml";
@ -56,6 +57,7 @@ Kirigami.ApplicationWindow {
: SettingsManager.lastOpenedPage === "QueuePage" ? 0
: SettingsManager.lastOpenedPage === "EpisodeSwipePage" ? 1
: SettingsManager.lastOpenedPage === "DownloadListPage" ? 0
: SettingsManager.lastOpenedPage === "DiscoverPage" ? 0
: 0
currentPage = SettingsManager.lastOpenedPage
pageStack.initialPage = getPage(SettingsManager.lastOpenedPage)
@ -103,6 +105,16 @@ Kirigami.ApplicationWindow {
tabBarActive = 0
}
},
Kirigami.Action {
text: i18n("Discover")
iconName: "search"
checked: currentPage == "DiscoverPage"
onTriggered: {
pushPage("DiscoverPage")
SettingsManager.lastOpenedPage = "DiscoverPage" // for persistency
tabBarActive = 0
}
},
Kirigami.Action {
text: i18n("Episodes")
iconName: "rss"

View File

@ -21,6 +21,7 @@
<file alias="EpisodeSwipePage.qml">qml/EpisodeSwipePage.qml</file>
<file alias="GenericHeader.qml">qml/GenericHeader.qml</file>
<file alias="GenericEntryDelegate.qml">qml/GenericEntryDelegate.qml</file>
<file alias="DiscoverPage.qml">qml/DiscoverPage.qml</file>
<file alias="ImageWithFallback.qml">qml/ImageWithFallback.qml</file>
<file alias="UpdateNotification.qml">qml/UpdateNotification.qml</file>
<file alias="HeaderBar.qml">qml/HeaderBar.qml</file>