Warning, /pim/itinerary/src/app/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 import QtCore as QtCore
0008 import QtQuick
0009 import QtQuick.Layouts
0010 import QtQuick.Controls as QQC2
0011 import QtQuick.Dialogs
0012 import QtLocation as QtLocation
0013 import org.kde.kirigami as Kirigami
0014 import org.kde.solidextras as Solid
0015 import org.kde.kpublictransport.onboard
0016 import internal.org.kde.kcalendarcore as KCalendarCore
0017 import org.kde.itinerary
0018 
0019 Kirigami.ApplicationWindow {
0020     id: root
0021     title: i18n("KDE Itinerary")
0022 
0023     width: Kirigami.Settings.isMobile ? 480 : 800
0024     height: Kirigami.Settings.isMobile ? 720 : 650
0025     minimumWidth: 300
0026     minimumHeight: 400
0027     Kirigami.PagePool {
0028         id: pagepool
0029     }
0030     pageStack {
0031         columnView.columnResizeMode: Kirigami.ColumnView.SingleColumn
0032 
0033         globalToolBar {
0034             style: Kirigami.Settings.isMobile ? Kirigami.ApplicationHeaderStyle.Breadcrumb : Kirigami.ApplicationHeaderStyle.Auto
0035             showNavigationButtons: Kirigami.ApplicationHeaderStyle.ShowBackButton
0036         }
0037     }
0038 
0039     // pop pages when not in use
0040     Connections {
0041         target: applicationWindow().pageStack
0042         function onCurrentIndexChanged() {
0043             // wait for animation to finish before popping pages
0044             timer.restart();
0045         }
0046     }
0047     
0048     Timer {
0049         id: timer
0050         interval: 300
0051         onTriggered: {
0052             let currentIndex = applicationWindow().pageStack.currentIndex;
0053             while (applicationWindow().pageStack.depth > (currentIndex + 1) && currentIndex >= 0) {
0054                 applicationWindow().pageStack.pop();
0055             }
0056         }
0057     }
0058 
0059     property list<Kirigami.Action> importActions: [
0060         Kirigami.Action {
0061             text: i18n("Open File...")
0062             icon.name: "document-open"
0063             shortcut: StandardKey.Open
0064             onTriggered: {
0065                 importFileDialog.open();
0066             }
0067         },
0068         Kirigami.Action {
0069             text: i18n("Paste")
0070             icon.name: "edit-paste"
0071             enabled: ApplicationController.hasClipboardContent
0072             shortcut: StandardKey.Paste
0073             onTriggered: {
0074                 ApplicationController.importFromClipboard();
0075             }
0076         },
0077         Kirigami.Action {
0078             text: i18n("Scan Barcode...")
0079             icon.name: "view-barcode-qr"
0080             onTriggered: {
0081                 pageStack.layers.push(scanBarcodeComponent);
0082             }
0083         },
0084         Kirigami.Action {
0085             icon.name: "view-calendar-day"
0086             text: i18n("Add from Calendar...")
0087             onTriggered: {
0088                 PermissionManager.requestPermission(Permission.ReadCalendar, function() {
0089                     if (!calendarSelector.model) {
0090                         calendarSelector.model = calendarModel.createObject(root);
0091                     }
0092                     calendarSelector.open();
0093                 })
0094             }
0095             visible: KCalendarCore.CalendarPluginLoader.hasPlugin
0096         },
0097         // TODO this should not be hardcoded here, but dynamically filled based on what online ticket
0098         // sources we support
0099         Kirigami.Action {
0100             text: i18n("Deutsche Bahn Online Ticket...")
0101             icon.name: "download"
0102             onTriggered: {
0103                 pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "OnlineImportPage"), {
0104                     source: "db",
0105                 });
0106             }
0107         },
0108         Kirigami.Action {
0109             text: i18n("SNCF Online Ticket...")
0110             icon.name: "download"
0111             onTriggered: {
0112                 pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "OnlineImportPage"), {
0113                     source: "sncf"
0114                 });
0115             }
0116         }
0117     ]
0118 
0119     FileDialog {
0120         id: importFileDialog
0121         fileMode: FileDialog.OpenFile
0122         title: i18n("Import Reservation")
0123         currentFolder: QtCore.StandardPaths.writableLocation(QtCore.StandardPaths.DocumentsLocation)
0124         // Android has no file type selector, we get the superset of all filters there since Qt6 (apart from "all"),
0125         // so don't set any filters on Android in order to be able to open everything we can read
0126         nameFilters:  Qt.platform.os === "android" ?
0127             [i18n("All Files (*.*)")] :
0128             [i18n("All Files (*.*)"), i18n("PkPass files (*.pkpass)"), i18n("PDF files (*.pdf)"), i18n("iCal events (*.ics)"), i18n("KDE Itinerary files (*.itinerary)")]
0129         onAccepted: ApplicationController.importFromUrl(selectedFile)
0130     }
0131 
0132     FileDialog {
0133         id: exportDialog
0134         fileMode: FileDialog.SaveFile
0135         title: i18n("Export Itinerary Data")
0136         currentFolder: QtCore.StandardPaths.writableLocation(QtCore.StandardPaths.DocumentsLocation)
0137         nameFilters: [i18n("KDE Itinerary files (*.itinerary)")]
0138         onAccepted: ApplicationController.exportToFile(selectedFile)
0139     }
0140 
0141     Component {
0142         id: calendarModel
0143         // needs to be created on demand, after we have calendar access permissions
0144         KCalendarCore.CalendarListModel {}
0145     }
0146     Component {
0147         id: calendarImportPage
0148         CalendarImportPage {}
0149     }
0150     footer: NavigationBar {
0151         id: navigationbar
0152     }
0153     CalendarSelectionSheet {
0154         id: calendarSelector
0155         // parent: root.Overlay.overlay
0156         onCalendarSelected: pageStack.push(calendarImportPage, { calendar: calendar });
0157     }
0158 
0159     OnboardStatus {
0160         id: onboardStatus
0161     }
0162 
0163     globalDrawer: Kirigami.GlobalDrawer {
0164         title: i18n("KDE Itinerary")
0165         titleIcon: "map-symbolic"
0166         isMenu: true
0167         actions: [
0168             Kirigami.Action {
0169                 text: i18n("Import...")
0170                 icon.name: "document-import"
0171                 children: importActions
0172             },
0173             Kirigami.Action {
0174                 text: i18n("Check for Updates")
0175                 icon.name: "view-refresh"
0176                 enabled: Solid.NetworkStatus.connectivity != Solid.NetworkStatus.No
0177                 shortcut: StandardKey.Refresh
0178                 onTriggered: {
0179                     LiveDataManager.checkForUpdates();
0180                 }
0181             },
0182             Kirigami.Action {
0183                 text: i18n("Download Maps")
0184                 icon.name: "download"
0185                 enabled: Solid.NetworkStatus.connectivity != Solid.NetworkStatus.No
0186                 icon.color: Solid.NetworkStatus.metered != Solid.NetworkStatus.No ? Kirigami.Theme.neutralTextColor : Kirigami.Theme.textColor
0187                 onTriggered: MapDownloadManager.download();
0188             },
0189             Kirigami.Action {
0190                 id: statsAction
0191                 text: i18n("Statistics")
0192                 icon.name: "view-statistics"
0193                 enabled: pageStack.layers.depth < 2
0194                 onTriggered: pageStack.layers.push(statisticsComponent)
0195             },
0196             Kirigami.Action {
0197                 id: healthCertAction
0198                 text: i18n("Health Certificates")
0199                 icon.name: "cross-shape"
0200                 onTriggered: {
0201                     healtCertificateComponent.source = Qt.resolvedUrl("HealthCertificatePage.qml")
0202                     pageStack.layers.push(healtCertificateComponent.item)
0203                 }
0204                 enabled: pageStack.layers.depth < 2
0205                 visible: ApplicationController.hasHealthCertificateSupport && ApplicationController.healthCertificateManager.rowCount() > 0
0206             },
0207             Kirigami.Action {
0208                 id: liveAction
0209                 text: i18n("Live Status")
0210                 icon.name: "media-playback-playing"
0211                 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "LiveStatusPage"))
0212                 enabled: pageStack.layers.depth < 2
0213                 visible: onboardStatus.status == OnboardStatus.Onboard
0214             },
0215             Kirigami.Action {
0216                 id: settingsAction
0217                 text: i18n("Settings...")
0218                 icon.name: "settings-configure"
0219                 enabled: pageStack.layers.depth < 2
0220                 shortcut: StandardKey.Preferences
0221                 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "SettingsPage"))
0222             },
0223             Kirigami.Action {
0224                 text: i18n("Export...")
0225                 icon.name: "export-symbolic"
0226                 onTriggered: exportDialog.open()
0227             },
0228             Kirigami.Action {
0229                 text: i18n("Help")
0230                 icon.name: "help-contents"
0231                 enabled: pageStack.layers.depth < 2
0232                 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "WelcomePage"))
0233             },
0234             Kirigami.Action {
0235                 id: aboutAction
0236                 text: i18n("About")
0237                 icon.name: "help-about-symbolic"
0238                 enabled: pageStack.layers.depth < 2
0239                 onTriggered: pageStack.pushDialogLayer(Qt.resolvedUrl("AboutPage.qml"))
0240             },
0241             Kirigami.Action {
0242                 id: devModeAction
0243                 text: "Development"
0244                 icon.name: "tools-report-bug"
0245                 enabled: pageStack.layers.depth < 2
0246                 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "DevelopmentModePage"));
0247                 visible: Settings.developmentMode
0248             }
0249         ]
0250     }
0251     contextDrawer: Kirigami.ContextDrawer {
0252         id: contextDrawer
0253         actions: {
0254             if (pageStack.layers.depth > 1)
0255                 return (pageStack.layers.currentItem as Kirigami.Page)?.actions ?? [];
0256             else (pageStack.currentItem as Kirigami.Page)?.actions ?? [];
0257         }
0258     }
0259     pageStack.initialPage: pagepool.loadPage(Qt.resolvedUrl("TimelinePage.qml"))
0260 
0261     DropArea {
0262         id: topDropArea
0263         onEntered: {
0264             if (!drag.hasUrls) {
0265                 drag.accepted = false;
0266             }
0267         }
0268         onDropped: {
0269             for (var i in drop.urls) {
0270                 ApplicationController.importFromUrl(drop.urls[i]);
0271             }
0272         }
0273         anchors.fill: parent
0274     }
0275 
0276     Connections {
0277         target: ApplicationController
0278         function onInfoMessage(msg) { showPassiveNotification(msg, "short"); }
0279         function onOpenPageRequested(page) {
0280             while (pageStack.layers.depth > 1) {
0281                 pageStack.layers.pop();
0282             }
0283             switch (page) {
0284             case "currentTicket":
0285                 pagepool.loadPage("TimelinePage.qml").showDetailsPageForReservation(TimelineModel.currentBatchId);
0286                 break;
0287             case "stats":
0288                 statsAction.trigger();
0289                 break;
0290             case "healthCert":
0291                 healthCertAction.trigger();
0292                 break;
0293             case "live":
0294                 liveAction.trigger();
0295                 break;
0296             default:
0297                 console.warn("Requested to open unknown page", page);
0298             }
0299         }
0300     }
0301 
0302     Connections {
0303         target: MapDownloadManager
0304         function onFinished() { showPassiveNotification(i18n("Map download finished."), "short"); }
0305     }
0306 
0307     Component.onCompleted: {
0308         if (ReservationManager.isEmpty()) {
0309             pageStack.push(Qt.createComponent("org.kde.itinerary", "WelcomePage"));
0310         }
0311     }
0312 
0313     Component {
0314         id: mainPageComponent
0315         TimelinePage {}
0316     }
0317     Component {
0318         id: scanBarcodeComponent
0319         BarcodeScannerPage {}
0320     }
0321     Component {
0322         id: statisticsComponent
0323         StatisticsPage {
0324             reservationManager: ReservationManager
0325             tripGroupManager: TripGroupManager
0326         }
0327     }
0328     Component {
0329         id: passComponent
0330         PassPage {}
0331     }
0332     // replace loader with component once we depend on KHealthCertificate unconditionally
0333     Loader {
0334         id: healtCertificateComponent
0335     }
0336     Component {
0337         id: journeySectionPage
0338         JourneySectionPage {}
0339     }
0340     Component {
0341         id: journeyPathPage
0342         JourneyPathPage {}
0343     }
0344     Component {
0345         id: indoorMapPage
0346         IndoorMapPage {}
0347     }
0348 
0349     // "singleton" OSM QtLocation plugin
0350     // we only want one of these, and created only when absolutely necessary
0351     // as this triggers network operations on creation already
0352     function osmPlugin() {
0353         if (!__qtLocationOSMPlugin) {
0354             __qtLocationOSMPlugin = __qtLocationOSMPluginComponent.createObject();
0355         }
0356         return __qtLocationOSMPlugin;
0357     }
0358     property var __qtLocationOSMPlugin: null
0359     Component {
0360         id: __qtLocationOSMPluginComponent
0361         QtLocation.Plugin {
0362             name: "osm"
0363             QtLocation.PluginParameter { name: "osm.useragent"; value: ApplicationController.userAgent }
0364             QtLocation.PluginParameter { name: "osm.mapping.providersrepository.address"; value: "https://autoconfig.kde.org/qtlocation/" }
0365         }
0366     }
0367 
0368     // workaround for Back key handling on Android causing the application to close
0369     // on secondary layers if those have no focus (either explicitly or via interaction)
0370     // ### if there isn't a proepr fix for this, should this happen in Kirigami instead?
0371     Connections {
0372         target: pageStack.layers
0373         function onCurrentItemChanged() {
0374             pageStack.layers.currentItem.forceActiveFocus();
0375         }
0376     }
0377 }