mirror of https://github.com/KDE/kasts.git
376 lines
14 KiB
QML
376 lines
14 KiB
QML
/**
|
|
* SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
|
* SPDX-FileCopyrightText: 2021-2022 Bart De Vries <bart@mogwai.be>
|
|
*
|
|
* 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.14 as Controls
|
|
import QtQuick.Layouts 1.14
|
|
import QtGraphicalEffects 1.15
|
|
|
|
import org.kde.kirigami 2.12 as Kirigami
|
|
|
|
import org.kde.kasts 1.0
|
|
|
|
Kirigami.ScrollablePage {
|
|
id: page
|
|
|
|
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
|
|
LayoutMirroring.childrenInherit: true
|
|
|
|
property QtObject feed;
|
|
property bool isSubscribed: true
|
|
property var subscribeAction: undefined // this is only used if instantiated from the discoverpage
|
|
|
|
property string author: isSubscribed ? (page.feed.authors.length === 0 ? "" : page.feed.authors[0].name) : feed.author
|
|
property bool showMoreInfo: false
|
|
|
|
title: i18n("Podcast Details")
|
|
|
|
Keys.onPressed: {
|
|
if (event.matches(StandardKey.Find)) {
|
|
searchActionButton.checked = true;
|
|
}
|
|
}
|
|
|
|
supportsRefreshing: true
|
|
|
|
onRefreshingChanged: {
|
|
if (refreshing) {
|
|
updateFeed.run()
|
|
}
|
|
}
|
|
|
|
// Overlay dialog box showing options what to do on metered connections
|
|
ConnectionCheckAction {
|
|
id: updateFeed
|
|
|
|
function action() {
|
|
feed.refresh()
|
|
}
|
|
|
|
function abortAction() {
|
|
page.refreshing = false
|
|
}
|
|
}
|
|
|
|
// Make sure that this feed is also showing as "refreshing" on FeedListPage
|
|
Connections {
|
|
target: feed
|
|
function onRefreshingChanged(refreshing) {
|
|
if(!refreshing)
|
|
page.refreshing = refreshing
|
|
}
|
|
}
|
|
|
|
actions.main: Kirigami.Action {
|
|
id: searchActionButton
|
|
icon.name: "search"
|
|
text: i18nc("@action:intoolbar", "Search and Filter")
|
|
checkable: true
|
|
enabled: page.feed.entries ? true : false
|
|
visible: enabled
|
|
onToggled: {
|
|
if (!checked && page.feed.entries) {
|
|
page.feed.entries.filterType = AbstractEpisodeProxyModel.NoFilter;
|
|
page.feed.entries.searchFilter = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
header: Loader {
|
|
anchors.right: parent.right
|
|
anchors.left: parent.left
|
|
|
|
active: searchActionButton.checked
|
|
visible: active
|
|
|
|
sourceComponent: SearchFilterBar {
|
|
proxyModel: page.feed.entries ? page.feed.entries : emptyListModel
|
|
parentKey: searchActionButton
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: entryListDelegate
|
|
GenericEntryDelegate {
|
|
listView: entryList
|
|
}
|
|
}
|
|
|
|
ListModel {
|
|
id: emptyListModel
|
|
}
|
|
|
|
GenericEntryListView {
|
|
id: entryList
|
|
reuseItems: true
|
|
currentIndex: -1
|
|
|
|
model: page.feed.entries ? page.feed.entries : emptyListModel
|
|
delegate: entryListDelegate
|
|
|
|
header: ColumnLayout {
|
|
id: headerColumn
|
|
height: (isSubscribed && entryList.count > 0) ? implicitHeight : entryList.height
|
|
width: entryList.width
|
|
spacing: 0
|
|
|
|
Kirigami.Theme.inherit: false
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
|
|
|
GenericHeader {
|
|
id: headerImage
|
|
Layout.fillWidth: true
|
|
|
|
image: isSubscribed ? feed.cachedImage : feed.image
|
|
title: isSubscribed ? feed.name : feed.title
|
|
subtitle: (!page.feed.authors || page.feed.authors.length === 0) ? "" : i18nc("by <author(s)>", "by %1", page.feed.authors[0].name)
|
|
}
|
|
|
|
// header actions
|
|
Controls.Control {
|
|
Layout.fillWidth: true
|
|
|
|
leftPadding: Kirigami.Units.largeSpacing
|
|
rightPadding: Kirigami.Units.largeSpacing
|
|
bottomPadding: Kirigami.Units.smallSpacing
|
|
topPadding: Kirigami.Units.smallSpacing
|
|
|
|
background: Rectangle {
|
|
Kirigami.Theme.inherit: false
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.Header
|
|
color: Kirigami.Theme.backgroundColor
|
|
}
|
|
|
|
contentItem: Kirigami.ActionToolBar {
|
|
id: feedToolBar
|
|
alignment: Qt.AlignLeft
|
|
background: Item {}
|
|
|
|
// HACK: ActionToolBar loads buttons dynamically, and so the
|
|
// height calculation changes the position
|
|
onHeightChanged: entryList.contentY = entryList.originY
|
|
|
|
actions: [
|
|
Kirigami.Action {
|
|
visible: isSubscribed
|
|
icon.name: "view-refresh"
|
|
text: i18n("Refresh Podcast")
|
|
onTriggered: page.refreshing = true
|
|
},
|
|
Kirigami.Action {
|
|
icon.name: "kt-add-feeds"
|
|
text: enabled ? i18n("Subscribe") : i18n("Subscribed")
|
|
enabled: !DataManager.feedExists(feed.url)
|
|
visible: !isSubscribed
|
|
onTriggered: {
|
|
DataManager.addFeed(feed.url);
|
|
enabled = false;
|
|
// Also disable button on discoverpage
|
|
if (subscribeAction !== undefined) {
|
|
subscribeAction.enabled = false;
|
|
}
|
|
}
|
|
},
|
|
Kirigami.Action {
|
|
icon.name: "documentinfo"
|
|
text: i18n("Show Details")
|
|
checkable: true
|
|
checked: showMoreInfo
|
|
onCheckedChanged: {
|
|
showMoreInfo = checked;
|
|
}
|
|
}
|
|
]
|
|
|
|
// add the default actions through onCompleted to add them
|
|
// to the ones defined above
|
|
Component.onCompleted: {
|
|
if (isSubscribed) {
|
|
for (var i in entryList.defaultActionList) {
|
|
feedToolBar.actions.push(entryList.defaultActionList[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Kirigami.Separator { Layout.fillWidth: true }
|
|
|
|
// podcast description
|
|
Controls.Control {
|
|
Layout.fillHeight: !isSubscribed
|
|
Layout.fillWidth: true
|
|
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
|
rightPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
|
topPadding: Kirigami.Units.largeSpacing
|
|
bottomPadding: Kirigami.Units.largeSpacing
|
|
|
|
// HACK: opening more info changes the position of the header
|
|
onHeightChanged: entryList.contentY = entryList.originY
|
|
|
|
background: Rectangle {
|
|
color: Kirigami.Theme.backgroundColor
|
|
}
|
|
|
|
contentItem: ColumnLayout {
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
Controls.Label {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
textFormat: page.showMoreInfo ? TextEdit.RichText : Text.StyledText
|
|
maximumLineCount: page.showMoreInfo ? undefined : 2
|
|
elide: Text.ElideRight
|
|
text: feed.description
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
|
wrapMode: Text.WordWrap
|
|
color: Kirigami.Theme.textColor
|
|
lineHeight: 1.2
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
Layout.preferredHeight: Math.max(feedUrlLayout.height, feedUrlCopyButton.width)
|
|
visible: page.showMoreInfo
|
|
height: visible ? implicitHeight : 0
|
|
RowLayout {
|
|
id: feedUrlLayout
|
|
anchors.left: parent.left
|
|
anchors.right: feedUrlCopyButton.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: Kirigami.Units.smallSpacing
|
|
TextEdit {
|
|
Layout.alignment: Qt.AlignTop
|
|
readOnly: true
|
|
textFormat:TextEdit.RichText
|
|
text: i18n("Podcast URL:")
|
|
wrapMode: TextEdit.Wrap
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
TextEdit {
|
|
id: feedUrl
|
|
Layout.alignment: Qt.AlignTop
|
|
readOnly: true
|
|
selectByMouse: !Kirigami.Settings.isMobile
|
|
textFormat:TextEdit.RichText
|
|
text: "<a href='%1'>%1</a>".arg(feed.url)
|
|
wrapMode: TextEdit.Wrap
|
|
Layout.fillWidth: true
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
}
|
|
Controls.Button {
|
|
id: feedUrlCopyButton
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
anchors.leftMargin: Kirigami.Units.smallSpacing
|
|
icon.name: "edit-copy"
|
|
|
|
onClicked: {
|
|
feedUrl.selectAll();
|
|
feedUrl.copy();
|
|
feedUrl.deselect();
|
|
}
|
|
}
|
|
}
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
visible: page.showMoreInfo
|
|
height: visible ? implicitHeight : 0
|
|
spacing: Kirigami.Units.smallSpacing
|
|
TextEdit {
|
|
Layout.alignment: Qt.AlignTop
|
|
readOnly: true
|
|
textFormat: TextEdit.RichText
|
|
text: i18n("Weblink:")
|
|
wrapMode: TextEdit.Wrap
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
|
|
TextEdit {
|
|
readOnly: true
|
|
Layout.alignment: Qt.AlignTop
|
|
selectByMouse: !Kirigami.Settings.isMobile
|
|
textFormat:TextEdit.RichText
|
|
text: "<a href='%1'>%1</a>".arg(feed.link)
|
|
onLinkActivated: Qt.openUrlExternally(link)
|
|
wrapMode: Text.WordWrap
|
|
Layout.fillWidth: true
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
}
|
|
TextEdit {
|
|
Layout.alignment: Qt.AlignTop
|
|
Layout.fillWidth: true
|
|
visible: isSubscribed && page.showMoreInfo
|
|
height: visible ? implicitHeight : 0
|
|
|
|
readOnly: true
|
|
selectByMouse: !Kirigami.Settings.isMobile
|
|
textFormat:TextEdit.RichText
|
|
text: isSubscribed ? i18n("Subscribed since: %1", feed.subscribed.toLocaleString(Qt.locale(), Locale.ShortFormat)) : ""
|
|
wrapMode: Text.WordWrap
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
TextEdit {
|
|
Layout.alignment: Qt.AlignTop
|
|
Layout.fillWidth: true
|
|
visible: isSubscribed && page.showMoreInfo
|
|
height: visible ? implicitHeight : 0
|
|
|
|
readOnly: true
|
|
selectByMouse: !Kirigami.Settings.isMobile
|
|
textFormat:TextEdit.RichText
|
|
text: isSubscribed ? i18n("Last Updated: %1", feed.lastUpdated.toLocaleString(Qt.locale(), Locale.ShortFormat)) : ""
|
|
wrapMode: Text.WordWrap
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
TextEdit {
|
|
Layout.alignment: Qt.AlignTop
|
|
Layout.fillWidth: true
|
|
visible: isSubscribed && page.showMoreInfo
|
|
height: visible ? implicitHeight : 0
|
|
|
|
readOnly: true
|
|
selectByMouse: !Kirigami.Settings.isMobile
|
|
textFormat:TextEdit.RichText
|
|
text: i18np("1 Episode", "%1 Episodes", feed.entryCount) + ", " + i18np("1 Unplayed", "%1 Unplayed", feed.unreadEntryCount)
|
|
wrapMode: Text.WordWrap
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
|
|
Item { Layout.fillHeight: true }
|
|
}
|
|
}
|
|
|
|
Kirigami.Separator { Layout.fillWidth: true }
|
|
|
|
Item {
|
|
Layout.fillHeight: true
|
|
Layout.fillWidth: true
|
|
height: visible ? implicitHeight : 0
|
|
visible: entryList.count === 0 && isSubscribed
|
|
|
|
Kirigami.PlaceholderMessage {
|
|
anchors.centerIn: parent
|
|
|
|
width: Kirigami.Units.gridUnit * 20
|
|
|
|
text: feed.errorId === 0 ? i18n("No Episodes Available") : i18n("Error (%1): %2", feed.errorId, feed.errorString)
|
|
icon.name: feed.errorId === 0 ? "" : "data-error"
|
|
}
|
|
}
|
|
}
|
|
|
|
FilterInlineMessage {
|
|
proxyModel: page.feed.entries ? page.feed.entries : emptyListModel
|
|
}
|
|
}
|
|
}
|