Warning, /plasma/discover/discover/qml/UpdatesPage.qml is written in an unsupported language. File is not indexed.

0001 import QtQuick.Controls 2.3
0002 import QtQuick.Layouts 1.1
0003 import QtQuick 2.15
0004 import org.kde.discover 2.0
0005 import org.kde.discover.app 1.0
0006 import "navigation.js" as Navigation
0007 import org.kde.kirigami 2.19 as Kirigami
0008 
0009 DiscoverPage
0010 {
0011     id: page
0012     title: i18n("Updates")
0013 
0014     property string footerLabel: ""
0015     property int footerProgress: 0
0016     property bool isBusy: false
0017     readonly property string name: title
0018 
0019     readonly property var resourcesUpdatesModel: ResourcesUpdatesModel {
0020         id: resourcesUpdatesModel
0021         onPassiveMessage: {
0022             sheet.errorMessage = message;
0023             sheet.sheetOpen = true;
0024         }
0025         onIsProgressingChanged: {
0026             if (!isProgressing) {
0027                 resourcesUpdatesModel.prepare()
0028             }
0029         }
0030 
0031         Component.onCompleted: {
0032             if (!isProgressing) {
0033                 resourcesUpdatesModel.prepare()
0034             }
0035         }
0036     }
0037 
0038     readonly property var sheet: Kirigami.OverlaySheet {
0039         id: sheet
0040 
0041         property string errorMessage: ""
0042 
0043         parent: applicationWindow().overlay
0044 
0045         title: contentLoader.sourceComponent === friendlyMessageComponent ? i18n("Update Issue") :  i18n("Technical details")
0046 
0047         Loader {
0048             id: contentLoader
0049             active: true
0050             sourceComponent: friendlyMessageComponent
0051 
0052             Component {
0053                 id: friendlyMessageComponent
0054 
0055                 ColumnLayout {
0056                     Label {
0057                         id: friendlyMessage
0058                         Layout.fillWidth: true
0059                         Layout.maximumWidth: Math.round(page.width*0.75)
0060                         Layout.bottomMargin: Kirigami.Units.largeSpacing * 2
0061                         text: i18n("There was an issue installing this update. Please try again later.")
0062                         wrapMode: Text.WordWrap
0063                     }
0064                     Button {
0065                         id: seeDetailsAndreportIssueButton
0066                         Layout.alignment: Qt.AlignRight
0067                         text: i18n("See Technical Details")
0068                         icon.name: "view-process-system"
0069                         onClicked: {
0070                             contentLoader.sourceComponent = nerdyDetailsComponent;
0071                         }
0072                     }
0073                 }
0074             }
0075 
0076             Component {
0077                 id: nerdyDetailsComponent
0078 
0079                 ColumnLayout {
0080                     Label {
0081                         Layout.fillWidth: true
0082                         Layout.maximumWidth: Math.round(page.width*0.75)
0083                         text: i18n("If you would like to report the update issue to your distribution's packagers, include this information:")
0084                         wrapMode: Text.WordWrap
0085                     }
0086 
0087                     TextArea {
0088                         Layout.fillWidth: true
0089                         text: sheet.errorMessage
0090                         textFormat: Text.RichText
0091                         wrapMode: Text.WordWrap
0092                     }
0093 
0094                     RowLayout {
0095                         Layout.alignment: Qt.AlignRight
0096 
0097                         Button {
0098                             text: i18n("Copy to Clipboard")
0099                             icon.name: "edit-copy"
0100                             onClicked: {
0101                                 app.copyTextToClipboard(sheet.errorMessage);
0102                                 window.showPassiveNotification(i18n("Error message copied to clipboard"));
0103                             }
0104                         }
0105 
0106                         Button {
0107                             text: i18n("Report This Issue")
0108                             icon.name: "tools-report-bug"
0109                             onClicked: {
0110                                 Qt.openUrlExternally(ResourcesModel.distroBugReportUrl())
0111                                 sheet.sheetOpen = false
0112                             }
0113                         }
0114                     }
0115                 }
0116             }
0117         }
0118 
0119         // Ensure that friendly message is shown if the user closes the sheet and
0120         // then opens it again
0121         onSheetOpenChanged: if (sheetOpen) {
0122             contentLoader.sourceComponent = friendlyMessageComponent;
0123         }
0124     }
0125 
0126     readonly property var updateModel: UpdateModel {
0127         id: updateModel
0128         backend: resourcesUpdatesModel
0129     }
0130 
0131     readonly property var updateAction: Kirigami.Action
0132     {
0133         id: updateAction
0134         text: page.unselected>0 ? i18n("Update Selected") : i18n("Update All")
0135         visible: updateModel.toUpdateCount
0136         iconName: "update-none"
0137 
0138         function anyVisible(items) {
0139             for (const itemPos in items) {
0140                 const item = items[itemPos];
0141                 if (item.visible && item instanceof Kirigami.InlineMessage) {
0142                     return true
0143                 }
0144             }
0145             return false;
0146         }
0147 
0148         enabled: !resourcesUpdatesModel.isProgressing && !ResourcesModel.isFetching && !anyVisible(page.header.children)
0149         onTriggered: resourcesUpdatesModel.updateAll()
0150     }
0151 
0152     header: ColumnLayout {
0153         id: errorsColumn
0154 
0155         DiscoverInlineMessage {
0156             Layout.fillWidth: true
0157             Layout.margins: Kirigami.Units.smallSpacing
0158             inlineMessage: ResourcesModel.inlineMessage
0159         }
0160 
0161         Repeater {
0162             model: resourcesUpdatesModel.errorMessages
0163             delegate: Kirigami.InlineMessage {
0164                 id: inline
0165                 Layout.fillWidth: true
0166                 Layout.margins: visible ? Kirigami.Units.smallSpacing : 0
0167                 text: modelData
0168                 visible: true
0169                 type: Kirigami.MessageType.Error
0170                 onVisibleChanged: errorsColumn.childrenChanged()
0171 
0172                 actions: [
0173                     Kirigami.Action {
0174                         icon.name: "dialog-cancel"
0175                         text: i18n("Ignore")
0176                         onTriggered: {
0177                             inline.visible = false
0178                         }
0179                     }
0180                 ]
0181             }
0182         }
0183     }
0184 
0185     footer: ColumnLayout {
0186         width: parent.width
0187         spacing: 0
0188 
0189         ScrollView {
0190             id: scv
0191             Layout.fillWidth: true
0192             Layout.preferredHeight: visible ? Kirigami.Units.gridUnit * 10 : 0
0193             visible: log.contents.length > 0
0194             TextArea {
0195                 readOnly: true
0196                 text: log.contents
0197 
0198                 cursorPosition: text.length - 1
0199                 font.family: "monospace"
0200 
0201                 ReadFile {
0202                     id: log
0203                     filter: ".*ALPM-SCRIPTLET\\] .*"
0204                     path: "/var/log/pacman.log"
0205                 }
0206             }
0207         }
0208 
0209         ToolBar {
0210             id: footerToolbar
0211             Layout.fillWidth: true
0212             visible: (updateModel.totalUpdatesCount > 0 && resourcesUpdatesModel.isProgressing) || updateModel.hasUpdates
0213 
0214             position: ToolBar.Footer
0215 
0216             contentItem: RowLayout {
0217                 ToolButton {
0218                     enabled: page.unselected > 0 && updateAction.enabled && !ResourcesModel.isFetching
0219                     visible: updateModel.totalUpdatesCount > 1 && !resourcesUpdatesModel.isProgressing
0220                     icon.name: "edit-select-all"
0221                     text: i18n("Select All")
0222                     onClicked: { updateModel.checkAll(); }
0223                 }
0224 
0225                 ToolButton {
0226                     enabled: page.unselected !== updateModel.totalUpdatesCount && updateAction.enabled && !ResourcesModel.isFetching
0227                     visible: updateModel.totalUpdatesCount > 1 && !resourcesUpdatesModel.isProgressing
0228                     icon.name: "edit-select-none"
0229                     text: i18n("Select None")
0230                     onClicked: { updateModel.uncheckAll(); }
0231                 }
0232 
0233                 CheckBox {
0234                     id: rebootAtEnd
0235                     visible: resourcesUpdatesModel.needsReboot && resourcesUpdatesModel.isProgressing
0236                     text: i18n("Restart automatically after update has completed");
0237                 }
0238 
0239                 Label {
0240                     Layout.fillWidth: true
0241                     Layout.rightMargin: Kirigami.Units.largeSpacing
0242                     horizontalAlignment: Text.AlignRight
0243                     text: i18n("Total size: %1", updateModel.updateSize)
0244                     elide: Text.ElideLeft
0245                 }
0246             }
0247         }
0248     }
0249 
0250     Kirigami.Action
0251     {
0252         id: cancelUpdateAction
0253         iconName: "dialog-cancel"
0254         text: i18n("Cancel")
0255         enabled: resourcesUpdatesModel.transaction && resourcesUpdatesModel.transaction.isCancellable
0256         onTriggered: resourcesUpdatesModel.transaction.cancel()
0257     }
0258 
0259     readonly property int unselected: (updateModel.totalUpdatesCount - updateModel.toUpdateCount)
0260 
0261     supportsRefreshing: true
0262     onRefreshingChanged: {
0263         ResourcesModel.updateAction.triggered()
0264         refreshing = false
0265     }
0266 
0267     readonly property Item report: ColumnLayout {
0268         parent: page
0269         anchors.fill: parent
0270         anchors.margins: Kirigami.Units.largeSpacing * 2
0271         Item {
0272             Layout.fillHeight: true
0273             width: 1
0274         }
0275 
0276         Kirigami.Action {
0277             id: restartAction
0278             icon.name: "system-reboot"
0279             text: i18n("Restart Now")
0280             visible: false
0281             onTriggered: app.reboot()
0282         }
0283         Kirigami.LoadingPlaceholder {
0284             id: statusLabel
0285             icon.name: {
0286                 if (page.footerProgress === 0 && page.footerLabel !== "" && !page.isBusy) {
0287                     return "update-none"
0288                 } else {
0289                     return ""
0290                 }
0291             }
0292             text: page.footerLabel
0293             determinate: true
0294             progressBar.value: page.footerProgress
0295         }
0296 
0297         Item {
0298             Layout.fillHeight: true
0299             width: 1
0300         }
0301     }
0302     ListView {
0303         id: updatesView
0304         currentIndex: -1
0305         reuseItems: true
0306         clip: true
0307 
0308         displaced: Transition {
0309             YAnimator {
0310                 duration: Kirigami.Units.longDuration
0311                 easing.type: Easing.InOutQuad
0312             }
0313         }
0314 
0315         model: QSortFilterProxyModel {
0316             sourceModel: updateModel
0317             sortRole: UpdateModel.SectionResourceProgressRole
0318         }
0319 
0320         section {
0321             property: "section"
0322             delegate: Kirigami.ListSectionHeader {
0323                 width: updatesView.width
0324                 label: section
0325             }
0326         }
0327 
0328         delegate: Kirigami.AbstractListItem {
0329             id: listItem
0330             highlighted: ListView.isCurrentItem
0331             hoverEnabled: !page.isBusy
0332             onEnabledChanged: if (!enabled) {
0333                 model.extended = false;
0334             }
0335 
0336             visible: resourceState < 3 //3=AbstractBackendUpdater.Done
0337 
0338             Keys.onReturnPressed: {
0339                 itemChecked.clicked()
0340             }
0341             Keys.onPressed: if (event.key===Qt.Key_Alt) model.extended = true
0342             Keys.onReleased: if (event.key===Qt.Key_Alt) model.extended = false
0343 
0344             ColumnLayout {
0345                 id: layout
0346                 property bool extended: model.extended
0347                 onExtendedChanged: if (extended) {
0348                     updateModel.fetchUpdateDetails(index)
0349                 }
0350                 RowLayout {
0351                     Layout.fillWidth: true
0352                     Layout.fillHeight: true
0353 
0354                     CheckBox {
0355                         id: itemChecked
0356                         Layout.alignment: Qt.AlignVCenter
0357                         checked: model.checked === Qt.Checked
0358                         onClicked: model.checked = (model.checked===Qt.Checked ? Qt.Unchecked : Qt.Checked)
0359                         enabled: !resourcesUpdatesModel.isProgressing
0360                     }
0361 
0362                     Kirigami.Icon {
0363                         width: Kirigami.Units.gridUnit * 2
0364                         Layout.preferredHeight: width
0365                         source: decoration
0366                         smooth: true
0367                     }
0368 
0369                     ColumnLayout {
0370                         Layout.fillWidth: true
0371                         Layout.leftMargin: Kirigami.Units.smallSpacing
0372                         Layout.alignment: Qt.AlignVCenter
0373 
0374                         spacing: 0
0375 
0376                         // App name
0377                         Kirigami.Heading {
0378                             Layout.fillWidth: true
0379                             text: i18n("%1", display)
0380                             level: 3
0381                             elide: Text.ElideRight
0382                         }
0383 
0384                         // Version numbers
0385                         Label {
0386                             Layout.fillWidth: true
0387                             elide: truncated ? Text.ElideLeft : Text.ElideRight
0388                             text: resource.upgradeText
0389                             opacity: listItem.hovered? 0.8 : 0.6
0390                         }
0391                     }
0392 
0393                     LabelBackground {
0394                         Layout.minimumWidth: Kirigami.Units.gridUnit * 6
0395                         text: resourceState == 2 ? i18n("Installing") : size
0396 
0397                         progress: resourceProgress/100
0398                     }
0399                 }
0400 
0401                 Frame {
0402                     Layout.fillWidth: true
0403                     implicitHeight: view.contentHeight
0404                     visible: layout.extended && changelog.length>0
0405                     Label {
0406                         id: view
0407                         anchors {
0408                             right: parent.right
0409                             left: parent.left
0410                         }
0411                         text: changelog
0412                         textFormat: Text.StyledText
0413                         wrapMode: Text.WordWrap
0414                         onLinkActivated: Qt.openUrlExternally(link)
0415 
0416                     }
0417 
0418                     //This saves a binding loop on implictHeight, as the Label
0419                     //height is updated twice (first time with the wrong value)
0420                     Behavior on implicitHeight
0421                     { PropertyAnimation { duration: Kirigami.Units.shortDuration } }
0422                 }
0423 
0424                 RowLayout {
0425                     Layout.fillWidth: true
0426                     visible: layout.extended
0427 
0428                     Label {
0429                         Layout.leftMargin: Kirigami.Units.gridUnit
0430                         text: i18n("Update from:")
0431                     }
0432                     // Backend icon
0433                     Kirigami.Icon {
0434                         source: resource.sourceIcon
0435                         implicitWidth: Kirigami.Units.iconSizes.smallMedium
0436                         implicitHeight: Kirigami.Units.iconSizes.smallMedium
0437                     }
0438                     // Backend label and origin/remote
0439                     Label {
0440                         Layout.fillWidth: true
0441                         text: resource.origin.length === 0 ? resource.backend.displayName
0442                                 : i18nc("%1 is the backend that provides this app, %2 is the specific repository or address within that backend","%1 (%2)",
0443                                         resource.backend.displayName, resource.origin)
0444                         elide: Text.ElideRight
0445                     }
0446 
0447                     Button {
0448                         Layout.alignment: Qt.AlignRight
0449                         text: i18n("More Information…")
0450                         enabled: !resourcesUpdatesModel.isProgressing
0451                         onClicked: Navigation.openApplication(resource)
0452                     }
0453                 }
0454             }
0455 
0456             onClicked: {
0457                 model.extended = !model.extended
0458             }
0459         }
0460     }
0461 
0462     readonly property alias secSinceUpdate: resourcesUpdatesModel.secsToLastUpdate
0463     state:  ( resourcesUpdatesModel.isProgressing        ? "progressing"
0464             : ResourcesModel.isFetching                  ? "fetching"
0465             : updateModel.hasUpdates                     ? "has-updates"
0466             : resourcesUpdatesModel.needsReboot          ? "reboot"
0467             : secSinceUpdate < 0                         ? "unknown"
0468             : secSinceUpdate === 0                       ? "now-uptodate"
0469             : secSinceUpdate < 1000 * 60 * 60 * 24       ? "uptodate"
0470             : secSinceUpdate < 1000 * 60 * 60 * 24 * 7   ? "medium"
0471             :                                              "low"
0472             )
0473 
0474     states: [
0475         State {
0476             name: "fetching"
0477             PropertyChanges { target: page; footerLabel: i18nc("@info", "Fetching updates…") }
0478             PropertyChanges { target: page; footerProgress: ResourcesModel.fetchingUpdatesProgress }
0479             PropertyChanges { target: page; isBusy: true }
0480             PropertyChanges { target: updatesView; opacity: 0 }
0481         },
0482         State {
0483             name: "progressing"
0484             PropertyChanges { target: page; supportsRefreshing: false }
0485             PropertyChanges { target: page.actions; main: cancelUpdateAction }
0486             PropertyChanges { target: statusLabel; visible: false }
0487         },
0488         State {
0489             name: "has-updates"
0490             PropertyChanges { target: page; title: i18nc("@info", "Updates") }
0491             // On mobile, we want "Update" to be the primary action so it's in
0492             // the center, but on desktop this feels a bit awkward and it would
0493             // be better to have "Update" be the right-most action
0494             PropertyChanges { target: page.actions; main: applicationWindow().wideScreen ? refreshAction : updateAction}
0495             PropertyChanges { target: page.actions; left: applicationWindow().wideScreen ? updateAction : refreshAction}
0496             PropertyChanges { target: statusLabel; visible: false }
0497         },
0498         State {
0499             name: "reboot"
0500             PropertyChanges { target: page; footerLabel: i18nc("@info", "Restart the system to complete the update process") }
0501             PropertyChanges { target: statusLabel; helpfulAction: restartAction }
0502             PropertyChanges { target: statusLabel; explanation: "" }
0503             PropertyChanges { target: statusLabel.progressBar; visible: false }
0504             StateChangeScript {
0505                 script: if (rebootAtEnd.checked && resourcesUpdatesModel.readyToReboot) {
0506                     app.rebootNow()
0507                 }
0508             }
0509         },
0510         State {
0511             name: "now-uptodate"
0512             PropertyChanges { target: page; footerLabel: i18nc("@info", "Up to date") }
0513             PropertyChanges { target: page.actions; main: refreshAction }
0514             PropertyChanges { target: statusLabel; explanation: "" }
0515             PropertyChanges { target: statusLabel.progressBar; visible: false }
0516         },
0517         State {
0518             name: "uptodate"
0519             PropertyChanges { target: page; footerLabel: i18nc("@info", "Up to date") }
0520             PropertyChanges { target: page.actions; main: refreshAction }
0521             PropertyChanges { target: statusLabel; explanation: "" }
0522             PropertyChanges { target: statusLabel.progressBar; visible: false }
0523         },
0524         State {
0525             name: "medium"
0526             PropertyChanges { target: page; title: i18nc("@info", "Up to date") }
0527             PropertyChanges { target: page.actions; main: refreshAction }
0528             PropertyChanges { target: statusLabel; explanation: "" }
0529             PropertyChanges { target: statusLabel.progressBar; visible: false }
0530         },
0531         State {
0532             name: "low"
0533             PropertyChanges { target: page; title: i18nc("@info", "Should check for updates") }
0534             PropertyChanges { target: page.actions; main: refreshAction }
0535             PropertyChanges { target: statusLabel; explanation: "" }
0536             PropertyChanges { target: statusLabel.progressBar; visible: false }
0537         },
0538         State {
0539             name: "unknown"
0540             PropertyChanges { target: page; title: i18nc("@info", "Time of last update unknown") }
0541             PropertyChanges { target: page.actions; main: refreshAction }
0542             PropertyChanges { target: statusLabel; explanation: "" }
0543             PropertyChanges { target: statusLabel.progressBar; visible: false }
0544         }
0545     ]
0546 }