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 }