Warning, /multimedia/kasts/src/qml/EntryPage.qml is written in an unsupported language. File is not indexed.

0001 /**
0002  * SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
0003  * SPDX-FileCopyrightText: 2021-2023 Bart De Vries <bart@mogwai.be>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 import QtQuick
0009 import QtQuick.Controls as Controls
0010 import QtQuick.Layouts
0011 
0012 import org.kde.kirigami as Kirigami
0013 import org.kde.kmediasession
0014 
0015 import org.kde.kasts
0016 import org.kde.kasts.settings
0017 
0018 Kirigami.ScrollablePage {
0019     id: page
0020 
0021     property QtObject entry
0022 
0023     title: i18nc("@title", "Episode Details")
0024 
0025     padding: 0  // needed to get the inline header to fill the page
0026 
0027     function openPodcast() {
0028         pushPage("FeedListPage");
0029         lastFeed = entry.feed.url;
0030         pageStack.push("qrc:/qt/qml/org/kde/kasts/qml/FeedDetailsPage.qml", {"feed": entry.feed});
0031     }
0032 
0033     // This function is needed to close the EntryPage if it is opened over the
0034     // QueuePage when the episode is removed from the queue (e.g. when the
0035     // episode finishes).
0036     Connections {
0037         target: entry
0038         function onQueueStatusChanged() {
0039             if (!entry.queueStatus) {
0040                 // this entry has just been removed from the queue
0041                 if (pageStack.depth > 1) {
0042                     if (pageStack.get(0).pageName === "queuepage") {
0043                         if (pageStack.get(0).lastEntry) {
0044                             if (pageStack.get(0).lastEntry === entry.id) {
0045                                 // if this EntryPage was open, then close it
0046                                 pageStack.pop()
0047                             }
0048                         }
0049                     }
0050                 }
0051             }
0052         }
0053     }
0054 
0055     Connections {
0056         target: entry.enclosure
0057         function onStatusChanged() {
0058             if (entry.enclosure.status === Enclosure.Downloadable) {
0059                 // this entry has just been deleted on the downloadpage
0060                 if (pageStack.depth > 1) {
0061                     if (pageStack.get(0).pageName === "downloadpage") {
0062                         if (pageStack.get(0).lastEntry) {
0063                             if (pageStack.get(0).lastEntry === entry.id) {
0064                                 // if this EntryPage was open, then close it
0065                                 pageStack.pop()
0066                             }
0067                         }
0068                     }
0069                 }
0070             }
0071         }
0072     }
0073 
0074     ColumnLayout {
0075         spacing: 0
0076 
0077         GenericHeader {
0078             id: infoHeader
0079             Layout.fillWidth: true
0080             image: entry.cachedImage
0081             title: entry.title
0082             subtitle: entry.feed.name
0083             subtitleClickable: true
0084 
0085             onSubtitleClicked: page.openPodcast()
0086         }
0087 
0088         // header actions
0089         Controls.Control {
0090             Layout.fillWidth: true
0091 
0092             leftPadding: Kirigami.Units.largeSpacing
0093             rightPadding: Kirigami.Units.largeSpacing
0094             bottomPadding: Kirigami.Units.smallSpacing
0095             topPadding: Kirigami.Units.smallSpacing
0096 
0097             background: Rectangle {
0098                 color: Kirigami.Theme.alternateBackgroundColor
0099 
0100                 Kirigami.Separator {
0101                     anchors.bottom: parent.bottom
0102                     anchors.left: parent.left
0103                     anchors.right: parent.right
0104                 }
0105             }
0106 
0107             contentItem: Kirigami.ActionToolBar {
0108                 alignment: Qt.AlignLeft
0109                 background: Item {}
0110 
0111                 actions: [
0112                     Kirigami.Action {
0113                         text: i18nc("@action:intoolbar Button to open an episode URL in browser", "Open in Browser")
0114                         visible: !entry.enclosure
0115                         icon.name: "globe"
0116                         onTriggered: {
0117                             Qt.openUrlExternally(entry.link);
0118                         }
0119                     },
0120                     Kirigami.Action {
0121                         text: i18nc("@action:intoolbar Button to start episode download", "Download")
0122                         visible: entry.enclosure && (entry.enclosure.status === Enclosure.Downloadable || entry.enclosure.status === Enclosure.PartiallyDownloaded)
0123                         icon.name: "download"
0124                         onTriggered: {
0125                             downloadOverlay.entry = entry;
0126                             downloadOverlay.run();
0127                         }
0128                     },
0129                     Kirigami.Action {
0130                         text: i18nc("@action:intoolbar Button to cancel ongoing download of episode", "Cancel Download")
0131                         visible: entry.enclosure && entry.enclosure.status === Enclosure.Downloading
0132                         icon.name: "edit-delete-remove"
0133                         onTriggered: {
0134                             entry.enclosure.cancelDownload();
0135                         }
0136                     },
0137                     Kirigami.Action {
0138                         text: i18nc("@action:intoolbar Button to pause the playback of the episode", "Pause")
0139                         visible: entry.enclosure && entry.queueStatus && (AudioManager.entry === entry && AudioManager.playbackState === KMediaSession.PlayingState)
0140                         icon.name: "media-playback-pause"
0141                         onTriggered: {
0142                             AudioManager.pause();
0143                         }
0144                     },
0145                     Kirigami.Action {
0146                         text: i18nc("@action:intoolbar Button to start playback of the episode", "Play")
0147                         visible: entry.enclosure && entry.enclosure.status === Enclosure.Downloaded && entry.queueStatus && (AudioManager.entry !== entry || AudioManager.playbackState !== KMediaSession.PlayingState)
0148                         icon.name: "media-playback-start"
0149                         onTriggered: {
0150                             AudioManager.entry = entry;
0151                             AudioManager.play();
0152                         }
0153                     },
0154                     Kirigami.Action {
0155                         text: i18nc("@action:intoolbar Action to start playback by streaming the episode rather than downloading it first", "Stream")
0156                         visible: entry.enclosure && entry.enclosure.status !== Enclosure.Downloaded && NetworkConnectionManager.streamingAllowed && (AudioManager.entry !== entry || AudioManager.playbackState !== KMediaSession.PlayingState)
0157                         icon.name: "media-playback-cloud"
0158                         onTriggered: {
0159                             if (!entry.queueStatus) {
0160                                 entry.queueStatus = true;
0161                             }
0162                             AudioManager.entry = entry;
0163                             AudioManager.play();
0164                         }
0165                     },
0166                     Kirigami.Action {
0167                         text: !entry.queueStatus ? i18nc("@action:intoolbar Button to add an episode to the play queue", "Add to Queue") : i18nc("@action:intoolbar Button to remove an episode from the play queue", "Remove from Queue")
0168                         icon.name: !entry.queueStatus ? "media-playlist-append" : "list-remove"
0169                         visible: entry.enclosure || entry.queueStatus
0170                         onTriggered: {
0171                             if(!entry.queueStatus) {
0172                                 entry.queueStatus = true;
0173                             } else {
0174                                 // first change to next track if this one is playing
0175                                 if (entry.hasEnclosure && entry === AudioManager.entry) {
0176                                     AudioManager.next()
0177                                 }
0178                                 entry.queueStatus = false
0179                             }
0180                         }
0181                     },
0182                     Kirigami.Action {
0183                         text: i18nc("@action:intoolbar Button to remove the downloaded episode audio file", "Delete Download")
0184                         icon.name: "delete"
0185                         visible: entry.enclosure && (entry.enclosure.status === Enclosure.Downloaded || entry.enclosure.status === Enclosure.PartiallyDownloaded)
0186                         onTriggered: {
0187                             entry.enclosure.deleteFile();
0188                         }
0189                     },
0190                     Kirigami.Action {
0191                         text: i18nc("@action:intoolbar Button to reset the play position of an episode to the start", "Reset Play Position")
0192                         visible: entry.enclosure && entry.enclosure.playPosition > 1000
0193                         onTriggered: entry.enclosure.playPosition = 0
0194                         displayHint: Kirigami.DisplayHint.AlwaysHide
0195                     },
0196                     Kirigami.Action {
0197                         text: entry.read ? i18nc("@action:intoolbar Button to mark eposide as not played", "Mark as Unplayed") : i18nc("@action:intoolbar Button to mark episode as played", "Mark as Played")
0198                         displayHint: Kirigami.DisplayHint.AlwaysHide
0199                         onTriggered: {
0200                             entry.read = !entry.read
0201                         }
0202                     },
0203                     Kirigami.Action {
0204                         text: entry.new ? i18nc("@action:intoolbar", "Remove \"New\" Label") : i18nc("@action:intoolbar", "Label as \"New\"")
0205                         displayHint: Kirigami.DisplayHint.AlwaysHide
0206                         onTriggered: {
0207                             entry.new = !entry.new
0208                         }
0209                     },
0210                     Kirigami.Action {
0211                         text: entry.favorite ? i18nc("@action:intoolbar Button to remove the \"favorite\" property of a podcast episode", "Remove from Favorites") : i18nc("@action:intoolbar Button to add a podcast episode as favorite", "Add to Favorites")
0212                         icon.name: !entry.favorite ? "starred-symbolic" : "non-starred-symbolic"
0213                         displayHint: Kirigami.DisplayHint.AlwaysHide
0214                         onTriggered: {
0215                             entry.favorite = !entry.favorite
0216                         }
0217                     },
0218                     Kirigami.Action {
0219                         text: i18nc("@action:intoolbar Button to open the podcast URL in browser", "Open Podcast")
0220                         displayHint: Kirigami.DisplayHint.AlwaysHide
0221                         onTriggered: page.openPodcast()
0222                     }
0223                 ]
0224             }
0225         }
0226 
0227         TextEdit {
0228             id: textLabel
0229             Layout.topMargin: Kirigami.Units.gridUnit
0230             Layout.leftMargin: Kirigami.Units.gridUnit
0231             Layout.rightMargin: Kirigami.Units.gridUnit
0232             Layout.bottomMargin: Kirigami.Units.gridUnit
0233             Layout.fillWidth: true
0234             Layout.fillHeight: true
0235 
0236             readOnly: true
0237             selectByMouse: !Kirigami.Settings.isMobile
0238             text: page.entry.content
0239             baseUrl: page.entry.baseUrl
0240             textFormat: Text.RichText
0241             wrapMode: Text.WordWrap
0242             font.pointSize: SettingsManager && !(SettingsManager.articleFontUseSystem) ? SettingsManager.articleFontSize : Kirigami.Theme.defaultFont.pointSize
0243             color: Kirigami.Theme.textColor
0244 
0245             onLinkActivated: (link) => {
0246                 if (link.split("://")[0] === "timestamp") {
0247                     if (AudioManager.entry && AudioManager.entry.enclosure && entry.enclosure && (entry.enclosure.status === Enclosure.Downloaded || SettingsManager.prioritizeStreaming)) {
0248                         if (AudioManager.entry !== entry) {
0249                             if (!entry.queueStatus) {
0250                                 entry.queueStatus = true;
0251                             }
0252                             AudioManager.entry = entry;
0253                             AudioManager.play();
0254                         }
0255                         AudioManager.seek(link.split("://")[1]);
0256                     }
0257                 } else {
0258                     Qt.openUrlExternally(link)
0259                 }
0260             }
0261             onLinkHovered: {
0262                 cursorShape: Qt.PointingHandCursor;
0263             }
0264             onWidthChanged: { text = entry.adjustedContent(width, font.pixelSize) }
0265         }
0266 
0267         ListView {
0268             visible: count !== 0
0269             Layout.fillWidth: true
0270             implicitHeight: contentHeight
0271             interactive: false
0272             currentIndex: -1
0273             Layout.leftMargin: Kirigami.Units.gridUnit
0274             Layout.rightMargin: Kirigami.Units.gridUnit
0275             Layout.bottomMargin: Kirigami.Units.gridUnit
0276             model: ChapterModel {
0277                 entry: page.entry
0278             }
0279             delegate: ChapterListDelegate {
0280                 entry: page.entry
0281             }
0282         }
0283 
0284         Controls.Button {
0285             Layout.leftMargin: Kirigami.Units.gridUnit
0286             Layout.rightMargin: Kirigami.Units.gridUnit
0287             Layout.bottomMargin: Kirigami.Units.gridUnit
0288             visible: entry.hasEnclosure
0289 
0290             text: i18nc("@action:button", "Copy Episode Download URL")
0291             height: enclosureUrl.height
0292             width: enclosureUrl.height
0293             icon.name: "edit-copy"
0294 
0295             onClicked: {
0296                 applicationWindow().showPassiveNotification(i18nc("@info:status", "Link Copied"));
0297                 enclosureUrl.selectAll();
0298                 enclosureUrl.copy();
0299                 enclosureUrl.deselect();
0300             }
0301 
0302             // copy url from this invisible textedit
0303             TextEdit {
0304                 id: enclosureUrl
0305                 visible: false
0306                 readOnly: true
0307                 textFormat:TextEdit.RichText
0308                 text: entry.hasEnclosure ? entry.enclosure.url : ""
0309                 color: Kirigami.Theme.textColor
0310             }
0311         }
0312     }
0313 }