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 }