Warning, /multimedia/kasts/src/qml/Settings/SynchronizationSettingsPage.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 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.kirigamiaddons.delegates as Delegates 0014 import org.kde.kirigamiaddons.formcard as FormCard 0015 0016 import org.kde.kasts 0017 import org.kde.kasts.settings 0018 0019 import ".." 0020 0021 Kirigami.ScrollablePage { 0022 id: root 0023 0024 leftPadding: 0 0025 rightPadding: 0 0026 0027 ColumnLayout { 0028 spacing: 0 0029 0030 FormCard.FormCard { 0031 Layout.fillWidth: true 0032 0033 FormCard.FormTextDelegate { 0034 id: accountStatus 0035 text: i18n("Account") 0036 description: Sync.syncEnabled ? i18n("Logged into account \"%1\" on server \"%2\"", Sync.username, (Sync.provider == SyncUtils.GPodderNet && Sync.hostname == "") ? "gpodder.net" : Sync.hostname) : i18n("Syncing disabled") 0037 0038 trailing: Controls.Button { 0039 text: Sync.syncEnabled ? i18n("Logout") : i18n("Login") 0040 onClicked: { 0041 Sync.syncEnabled ? Sync.logout() : syncProviderOverlay.open(); 0042 } 0043 } 0044 } 0045 0046 FormCard.FormDelegateSeparator {} 0047 0048 FormCard.FormTextDelegate { 0049 id: manualSync 0050 text: i18n("Manually sync") 0051 0052 trailing: Controls.Button { 0053 text: i18n("Sync Now") 0054 enabled: Sync.syncEnabled 0055 onClicked: { 0056 syncFeedsAndEpisodes.run(); 0057 } 0058 } 0059 } 0060 0061 FormCard.FormDelegateSeparator {} 0062 0063 FormCard.FormTextDelegate { 0064 id: lastFullSync 0065 text: i18n("Last full sync with server") 0066 description: Sync.lastSuccessfulDownloadSync 0067 } 0068 0069 FormCard.FormDelegateSeparator {} 0070 0071 FormCard.FormTextDelegate { 0072 id: lastQuickUpload 0073 text: i18n("Last quick upload to sync server") 0074 description: Sync.lastSuccessfulUploadSync 0075 } 0076 } 0077 0078 FormCard.FormHeader { 0079 title: i18n("Automatic syncing") 0080 Layout.fillWidth: true 0081 } 0082 0083 FormCard.FormCard { 0084 Layout.fillWidth: true 0085 0086 FormCard.FormCheckDelegate { 0087 enabled: Sync.syncEnabled 0088 checked: SettingsManager.refreshOnStartup 0089 text: i18n("Do full sync on startup") 0090 onToggled: { 0091 SettingsManager.refreshOnStartup = checked; 0092 SettingsManager.save(); 0093 } 0094 } 0095 0096 FormCard.FormCheckDelegate { 0097 enabled: Sync.syncEnabled 0098 checked: SettingsManager.syncWhenUpdatingFeeds 0099 text: i18n("Do full sync when fetching podcasts") 0100 onToggled: { 0101 SettingsManager.syncWhenUpdatingFeeds = checked; 0102 SettingsManager.save(); 0103 } 0104 } 0105 0106 FormCard.FormCheckDelegate { 0107 enabled: Sync.syncEnabled 0108 checked: SettingsManager.syncWhenPlayerstateChanges 0109 text: i18n("Upload episode play positions on play/pause toggle") 0110 onToggled: { 0111 SettingsManager.syncWhenPlayerstateChanges = checked; 0112 SettingsManager.save(); 0113 } 0114 } 0115 } 0116 0117 FormCard.FormHeader { 0118 title: i18n("Advanced options") 0119 Layout.fillWidth: true 0120 } 0121 0122 FormCard.FormCard { 0123 Layout.fillWidth: true 0124 0125 FormCard.FormTextDelegate { 0126 id: fetchAllEpisodeStates 0127 text: i18n("Fetch all episode states from server") 0128 0129 trailing: Controls.Button { 0130 text: i18n("Fetch") 0131 enabled: Sync.syncEnabled 0132 onClicked: { 0133 forceSyncFeedsAndEpisodes.run(); 0134 } 0135 } 0136 } 0137 0138 FormCard.FormDelegateSeparator {} 0139 0140 FormCard.FormTextDelegate { 0141 id: fetchLocalEpisodeStates 0142 text: i18n("Push all local episode states to server") 0143 0144 trailing: Controls.Button { 0145 enabled: Sync.syncEnabled 0146 text: i18n("Push") 0147 onClicked: { 0148 syncPushAllStatesDialog.open(); 0149 } 0150 } 0151 } 0152 } 0153 } 0154 0155 // This item can be used to trigger an update of all feeds; it will open an 0156 // overlay with options in case the operation is not allowed by the settings 0157 ConnectionCheckAction { 0158 id: syncFeedsAndEpisodes 0159 0160 function action() { 0161 Sync.doRegularSync(); 0162 } 0163 } 0164 0165 // This item can be used to trigger an update of all feeds; it will open an 0166 // overlay with options in case the operation is not allowed by the settings 0167 ConnectionCheckAction { 0168 id: forceSyncFeedsAndEpisodes 0169 0170 function action() { 0171 Sync.doForceSync(); 0172 } 0173 } 0174 0175 Kirigami.Dialog { 0176 id: syncPushAllStatesDialog 0177 preferredWidth: Kirigami.Units.gridUnit * 25 0178 padding: Kirigami.Units.largeSpacing 0179 0180 showCloseButton: true 0181 standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel 0182 closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside 0183 0184 title: i18n("Push all local episode states to server?") 0185 0186 onAccepted: { 0187 syncPushAllStatesDialog.close(); 0188 syncPushAllStates.run(); 0189 } 0190 onRejected: syncPushAllStatesDialog.close(); 0191 0192 RowLayout { 0193 spacing: Kirigami.Units.largeSpacing 0194 Kirigami.Icon { 0195 Layout.preferredHeight: Kirigami.Units.gridUnit * 4 0196 Layout.preferredWidth: Kirigami.Units.gridUnit * 4 0197 source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" 0198 } 0199 TextEdit { 0200 Layout.fillWidth: true 0201 Layout.fillHeight: true 0202 readOnly: true 0203 wrapMode: Text.WordWrap 0204 text: i18n("Please note that pushing the playback state of all local episodes to the server might take a very long time and/or might overload the server. Also note that this action will overwrite all existing episode states on the server.\n\nContinue?") 0205 color: Kirigami.Theme.textColor 0206 Keys.onReturnPressed: accepted(); 0207 } 0208 } 0209 } 0210 0211 // This item can be used to trigger a push of all episode states to the server; 0212 // it will open an overlay with options in case the operation is not allowed by the settings 0213 ConnectionCheckAction { 0214 id: syncPushAllStates 0215 0216 function action() { 0217 Sync.doSyncPushAll(); 0218 } 0219 } 0220 0221 Kirigami.Dialog { 0222 id: syncProviderOverlay 0223 preferredWidth: Kirigami.Units.gridUnit * 20 0224 standardButtons: Kirigami.Dialog.NoButton 0225 0226 showCloseButton: true 0227 0228 title: i18n("Select Sync Provider") 0229 0230 ColumnLayout { 0231 spacing: 0 0232 0233 Repeater { 0234 focus: syncProviderOverlay.visible 0235 0236 model: ListModel { 0237 id: providerModel 0238 } 0239 Component.onCompleted: { 0240 providerModel.append({"name": i18n("gpodder.net"), 0241 "subtitle": i18n("Synchronize with official gpodder.net server"), 0242 "icon": "gpodder", 0243 "provider": Sync.GPodderNet}); 0244 providerModel.append({"name": i18n("GPodder Nextcloud"), 0245 "subtitle": i18n("Synchronize with GPodder Nextcloud app"), 0246 "icon": "kaccounts-nextcloud", 0247 "provider": Sync.GPodderNextcloud}); 0248 } 0249 delegate: Delegates.RoundedItemDelegate { 0250 id: syncProviderRepeaterDelegate 0251 Layout.fillWidth: true 0252 text: model.name 0253 icon.name: model.icon 0254 contentItem: Delegates.SubtitleContentItem { 0255 itemDelegate: syncProviderRepeaterDelegate 0256 subtitle: model.subtitle 0257 } 0258 Keys.onReturnPressed: clicked() 0259 onClicked: { 0260 Sync.provider = model.provider; 0261 syncProviderOverlay.close(); 0262 syncLoginOverlay.open(); 0263 } 0264 } 0265 } 0266 } 0267 } 0268 0269 Kirigami.Dialog { 0270 id: syncLoginOverlay 0271 preferredWidth: Kirigami.Units.gridUnit * 25 0272 padding: Kirigami.Units.largeSpacing 0273 0274 showCloseButton: true 0275 standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel 0276 closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside 0277 0278 title: i18n("Sync Login Credentials") 0279 0280 onAccepted: { 0281 if (Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked) { 0282 Sync.hostname = hostnameField.text; 0283 } else { 0284 Sync.hostname = "" 0285 } 0286 Sync.login(usernameField.text, passwordField.text); 0287 syncLoginOverlay.close(); 0288 } 0289 onRejected: syncLoginOverlay.close(); 0290 0291 Column { 0292 spacing: Kirigami.Units.largeSpacing 0293 RowLayout { 0294 width: parent.width 0295 spacing: Kirigami.Units.largeSpacing 0296 Kirigami.Icon { 0297 Layout.preferredHeight: Kirigami.Units.gridUnit * 4 0298 Layout.preferredWidth: Kirigami.Units.gridUnit * 4 0299 source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" 0300 } 0301 ColumnLayout { 0302 Layout.fillWidth: true 0303 Layout.fillHeight: true 0304 Kirigami.Heading { 0305 clip: true 0306 level: 2 0307 text: Sync.provider === Sync.GPodderNextcloud ? i18n("Sync with GPodder Nextcloud app") : i18n("Sync with gpodder.net service") 0308 } 0309 TextEdit { 0310 Layout.fillWidth: true 0311 readOnly: true 0312 wrapMode: Text.WordWrap 0313 textFormat: Text.RichText 0314 onLinkActivated: (link) => { 0315 Qt.openUrlExternally(link); 0316 } 0317 text: Sync.provider === Sync.GPodderNextcloud ? 0318 i18nc("argument is a weblink", "Sync with a Nextcloud server that has the GPodder Sync app installed: %1.<br/>It is advised to manually create an app password for Kasts through the web interface and use those credentials." , "<a href=\"https://apps.nextcloud.com/apps/gpoddersync\">https://apps.nextcloud.com/apps/gpoddersync</a>") : 0319 i18nc("argument is a weblink", "If you don't already have an account, you should first create one at %1", "<a href=\"https://gpodder.net\">https://gpodder.net</a>") 0320 color: Kirigami.Theme.textColor 0321 } 0322 } 0323 } 0324 GridLayout { 0325 width: parent.width 0326 columns: 2 0327 rowSpacing: Kirigami.Units.smallSpacing 0328 columnSpacing: Kirigami.Units.smallSpacing 0329 Controls.Label { 0330 Layout.alignment: Qt.AlignRight 0331 text: i18n("Username:") 0332 } 0333 Controls.TextField { 0334 id: usernameField 0335 Layout.fillWidth: true 0336 text: Sync.username 0337 Keys.onReturnPressed: syncLoginOverlay.accepted(); 0338 // focus: syncLoginOverlay.visible // disabled for now since it causes problem with virtual keyboard appearing at the same time as the overlay 0339 } 0340 Controls.Label { 0341 Layout.alignment: Qt.AlignRight 0342 text: i18n("Password:") 0343 } 0344 Controls.TextField { 0345 id: passwordField 0346 Layout.fillWidth: true 0347 echoMode: TextInput.Password 0348 text: Sync.password 0349 Keys.onReturnPressed: syncLoginOverlay.accepted(); 0350 } 0351 Controls.CheckBox { 0352 id: customServerCheckBox 0353 Layout.row: 2 0354 Layout.column: 1 0355 visible: Sync.provider === Sync.GPodderNet 0356 checked: false 0357 text: i18n("Use custom server") 0358 } 0359 Controls.Label { 0360 visible: Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked 0361 Layout.alignment: Qt.AlignRight 0362 text: i18n("Hostname:") 0363 } 0364 Controls.TextField { 0365 visible: Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked 0366 id: hostnameField 0367 Layout.fillWidth: true 0368 placeholderText: Sync.provider === Sync.GPodderNet ? "https://gpodder.net" : "https://nextcloud.mydomain.org" 0369 text: Sync.hostname 0370 Keys.onReturnPressed: syncLoginOverlay.accepted(); 0371 } 0372 } 0373 } 0374 } 0375 0376 Connections { 0377 target: Sync 0378 function onDeviceListReceived() { 0379 syncDeviceOverlay.open(); 0380 syncDeviceOverlay.update(); 0381 } 0382 function onLoginSucceeded() { 0383 if (Sync.provider === Sync.GPodderNextcloud) { 0384 firstSyncOverlay.open(); 0385 } 0386 } 0387 } 0388 0389 Kirigami.Dialog { 0390 id: syncDeviceOverlay 0391 preferredWidth: Kirigami.Units.gridUnit * 25 0392 padding: Kirigami.Units.largeSpacing 0393 0394 showCloseButton: true 0395 0396 title: i18n("Sync Device Settings") 0397 0398 Column { 0399 spacing: Kirigami.Units.largeSpacing * 2 0400 Kirigami.Heading { 0401 level: 2 0402 text: i18n("Create a new device") 0403 } 0404 GridLayout { 0405 columns: 2 0406 width: parent.width 0407 Controls.Label { 0408 text: i18n("Device Name:") 0409 } 0410 Controls.TextField { 0411 id: deviceField 0412 Layout.fillWidth: true 0413 text: Sync.suggestedDevice 0414 Keys.onReturnPressed: createDeviceButton.clicked(); 0415 // focus: syncDeviceOverlay.visible // disabled for now since it causes problem with virtual keyboard appearing at the same time as the overlay 0416 } 0417 Controls.Label { 0418 text: i18n("Device Description:") 0419 } 0420 Controls.TextField { 0421 id: deviceNameField 0422 Layout.fillWidth: true 0423 text: Sync.suggestedDeviceName 0424 Keys.onReturnPressed: createDeviceButton.clicked(); 0425 } 0426 Controls.Label { 0427 text: i18n("Device Type:") 0428 } 0429 Controls.ComboBox { 0430 id: deviceTypeField 0431 textRole: "text" 0432 valueRole: "value" 0433 popup.z: 102 // popup has to go in front of OverlaySheet 0434 model: [{"text": i18n("other"), "value": "other"}, 0435 {"text": i18n("desktop"), "value": "desktop"}, 0436 {"text": i18n("laptop"), "value": "laptop"}, 0437 {"text": i18n("server"), "value": "server"}, 0438 {"text": i18n("mobile"), "value": "mobile"}] 0439 } 0440 } 0441 Controls.Button { 0442 id: createDeviceButton 0443 text: i18n("Create Device") 0444 icon.name: "list-add" 0445 onClicked: { 0446 Sync.registerNewDevice(deviceField.text, deviceNameField.text, deviceTypeField.currentValue); 0447 syncDeviceOverlay.close(); 0448 } 0449 } 0450 ListView { 0451 id: deviceList 0452 width: parent.width 0453 height: contentItem.childrenRect.height 0454 visible: deviceListModel.count !== 0 0455 0456 header: Kirigami.Heading { 0457 topPadding: Kirigami.Units.gridUnit 0458 bottomPadding: Kirigami.Units.largeSpacing 0459 level: 2 0460 text: i18n("or select an existing device") 0461 } 0462 model: ListModel { 0463 id: deviceListModel 0464 } 0465 0466 delegate: Delegates.RoundedItemDelegate { 0467 text: model.device.caption 0468 icon.name: model.device.type == "desktop" ? "computer" : 0469 model.device.type == "laptop" ? "computer-laptop" : 0470 model.device.type == "server" ? "network-server-database" : 0471 model.device.type == "mobile" ? "smartphone" : 0472 "emblem-music-symbolic" 0473 onClicked: { 0474 syncDeviceOverlay.close(); 0475 Sync.device = model.device.id; 0476 Sync.deviceName = model.device.caption; 0477 Sync.syncEnabled = true; 0478 syncGroupOverlay.open(); 0479 } 0480 } 0481 } 0482 } 0483 0484 function update() { 0485 deviceListModel.clear(); 0486 for (var index in Sync.deviceList) { 0487 deviceListModel.append({"device": Sync.deviceList[index]}); 0488 } 0489 } 0490 } 0491 0492 Connections { 0493 target: Sync 0494 function onDeviceCreated() { 0495 syncGroupOverlay.open(); 0496 } 0497 } 0498 0499 Kirigami.Dialog { 0500 id: syncGroupOverlay 0501 preferredWidth: Kirigami.Units.gridUnit * 25 0502 padding: Kirigami.Units.largeSpacing 0503 0504 showCloseButton: true 0505 standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel 0506 closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside 0507 0508 title: i18n("Device Sync Settings") 0509 0510 onAccepted: { 0511 Sync.linkUpAllDevices(); 0512 syncGroupOverlay.close(); 0513 } 0514 onRejected: { 0515 syncGroupOverlay.close(); 0516 } 0517 0518 RowLayout { 0519 spacing: Kirigami.Units.largeSpacing 0520 Kirigami.Icon { 0521 Layout.preferredHeight: Kirigami.Units.gridUnit * 4 0522 Layout.preferredWidth: Kirigami.Units.gridUnit * 4 0523 source: "gpodder" 0524 } 0525 TextEdit { 0526 Layout.fillWidth: true 0527 Layout.fillHeight: true 0528 readOnly: true 0529 wrapMode: Text.WordWrap 0530 text: i18n("Should all podcast subscriptions on this gpodder.net account be synced across all devices?\nIf you don't know what this means, you should probably select \"Ok\".") 0531 color: Kirigami.Theme.textColor 0532 Keys.onReturnPressed: accepted(); 0533 } 0534 } 0535 0536 onVisibleChanged: { 0537 if (!visible) { 0538 firstSyncOverlay.open(); 0539 } 0540 } 0541 } 0542 0543 Kirigami.Dialog { 0544 id: firstSyncOverlay 0545 preferredWidth: Kirigami.Units.gridUnit * 16 0546 padding: Kirigami.Units.largeSpacing 0547 0548 showCloseButton: true 0549 standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel 0550 closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside 0551 0552 title: i18n("Sync Now?") 0553 0554 onAccepted: { 0555 firstSyncOverlay.close(); 0556 Sync.doRegularSync(); 0557 } 0558 onRejected: firstSyncOverlay.close(); 0559 0560 RowLayout { 0561 spacing: Kirigami.Units.largeSpacing 0562 Kirigami.Icon { 0563 Layout.preferredHeight: Kirigami.Units.gridUnit * 4 0564 Layout.preferredWidth: Kirigami.Units.gridUnit * 4 0565 source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" 0566 } 0567 TextEdit { 0568 Layout.fillWidth: true 0569 Layout.fillHeight: true 0570 readOnly: true 0571 wrapMode: Text.WordWrap 0572 text: i18n("Perform a first sync now?") 0573 color: Kirigami.Theme.textColor 0574 Keys.onReturnPressed: accepted(); 0575 } 0576 } 0577 } 0578 }