diff --git a/src/qml/Settings/GeneralSettingsPage.qml b/src/qml/Settings/GeneralSettingsPage.qml index b85e9953..35700f88 100644 --- a/src/qml/Settings/GeneralSettingsPage.qml +++ b/src/qml/Settings/GeneralSettingsPage.qml @@ -15,7 +15,6 @@ import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm import org.kde.kasts 1.0 Kirigami.ScrollablePage { - id: page title: i18n("General Settings") leftPadding: 0 @@ -28,7 +27,6 @@ Kirigami.ScrollablePage { ColumnLayout { spacing: 0 - width: page.width MobileForm.FormCard { Layout.fillWidth: true diff --git a/src/qml/Settings/NetworkSettingsPage.qml b/src/qml/Settings/NetworkSettingsPage.qml index da06401b..b64d0924 100644 --- a/src/qml/Settings/NetworkSettingsPage.qml +++ b/src/qml/Settings/NetworkSettingsPage.qml @@ -15,7 +15,6 @@ import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm import org.kde.kasts 1.0 Kirigami.ScrollablePage { - id: page title: i18n("Network Settings") leftPadding: 0 @@ -28,7 +27,6 @@ Kirigami.ScrollablePage { ColumnLayout { spacing: 0 - width: page.width MobileForm.FormCard { Layout.fillWidth: true diff --git a/src/qml/Settings/StorageSettingsPage.qml b/src/qml/Settings/StorageSettingsPage.qml index e724efb1..a363f049 100644 --- a/src/qml/Settings/StorageSettingsPage.qml +++ b/src/qml/Settings/StorageSettingsPage.qml @@ -16,7 +16,6 @@ import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm import org.kde.kasts 1.0 Kirigami.ScrollablePage { - id: page title: i18n("Storage Settings") leftPadding: 0 @@ -29,7 +28,6 @@ Kirigami.ScrollablePage { ColumnLayout { spacing: 0 - width: page.width MobileForm.FormCard { Layout.fillWidth: true diff --git a/src/qml/Settings/SynchronizationSettingsPage.qml b/src/qml/Settings/SynchronizationSettingsPage.qml index a585ac07..279a36bc 100644 --- a/src/qml/Settings/SynchronizationSettingsPage.qml +++ b/src/qml/Settings/SynchronizationSettingsPage.qml @@ -10,518 +10,570 @@ import QtQuick.Controls 2.14 as Controls import QtQuick.Layouts 1.14 import org.kde.kirigami 2.19 as Kirigami +import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm import org.kde.kasts 1.0 Kirigami.ScrollablePage { title: i18n("Synchronization Settings") - Kirigami.FormLayout { + leftPadding: 0 + rightPadding: 0 + topPadding: Kirigami.Units.gridUnit + bottomPadding: Kirigami.Units.gridUnit - Controls.Label { - Kirigami.FormData.label: i18n("Current Status:") - text: 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") - wrapMode: Text.WordWrap - } + Kirigami.Theme.colorSet: Kirigami.Theme.Window + Kirigami.Theme.inherit: false - Controls.Label { - Kirigami.FormData.label: i18n("Last full sync with server:") - text: Sync.lastSuccessfulDownloadSync - wrapMode: Text.WordWrap - } + ColumnLayout { + spacing: 0 - Controls.Label { - Kirigami.FormData.label: i18n("Last quick upload to sync server:") - text: Sync.lastSuccessfulUploadSync - wrapMode: Text.WordWrap - } + MobileForm.FormCard { + Layout.fillWidth: true - Controls.Button { - text: i18n("Login") - enabled: !Sync.syncEnabled - onClicked: syncProviderOverlay.open() - } - - Kirigami.Dialog { - id: syncProviderOverlay - preferredWidth: Kirigami.Units.gridUnit * 20 - standardButtons: Kirigami.Dialog.NoButton - - showCloseButton: true - - title: i18n("Select Sync Provider") - - ColumnLayout { + contentItem: ColumnLayout { spacing: 0 - Repeater { - focus: syncProviderOverlay.visible + MobileForm.FormTextDelegate { + id: accountStatus + text: i18n("Account") + 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") - model: ListModel { - id: providerModel - } - Component.onCompleted: { - providerModel.append({"name": i18n("gpodder.net"), - "subtitle": i18n("Synchronize with official gpodder.net server"), - "icon": "gpodder", - "provider": Sync.GPodderNet}); - providerModel.append({"name": i18n("GPodder Nextcloud"), - "subtitle": i18n("Synchronize with GPodder Nextcloud app"), - "icon": "kaccounts-nextcloud", - "provider": Sync.GPodderNextcloud}); - } - delegate: Kirigami.BasicListItem { - Layout.fillWidth: true - label: model.name - subtitle: model.subtitle - icon: model.icon - //highlighted: false - iconSize: Kirigami.Units.gridUnit * 3 - Keys.onReturnPressed: clicked() + trailing: Controls.Button { + text: Sync.syncEnabled ? i18n("Logout") : i18n("Login") onClicked: { - Sync.provider = model.provider; - syncProviderOverlay.close(); - syncLoginOverlay.open(); + Sync.syncEnabled ? Sync.logout() : syncProviderOverlay.open(); + } + } + } + + MobileForm.FormDelegateSeparator { above: accountStatus; below: manualSync } + + MobileForm.FormTextDelegate { + id: manualSync + text: i18n("Manually sync") + + trailing: Controls.Button { + text: i18n("Sync Now") + enabled: Sync.syncEnabled + onClicked: { + syncFeedsAndEpisodes.run(); + } + } + } + + MobileForm.FormDelegateSeparator { above: manualSync; below: lastFullSync } + + MobileForm.FormTextDelegate { + id: lastFullSync + text: i18n("Last full sync with server") + description: Sync.lastSuccessfulDownloadSync + } + + MobileForm.FormDelegateSeparator { above: lastFullSync; below: lastQuickUpload } + + MobileForm.FormTextDelegate { + id: lastQuickUpload + text: i18n("Last quick upload to sync server") + description: Sync.lastSuccessfulUploadSync + } + } + } + + MobileForm.FormCard { + Layout.topMargin: Kirigami.Units.largeSpacing + Layout.fillWidth: true + + contentItem: ColumnLayout { + spacing: 0 + + MobileForm.FormCardHeader { + title: i18n("Automatic syncing") + } + + MobileForm.FormCheckDelegate { + enabled: Sync.syncEnabled + checked: SettingsManager.refreshOnStartup + text: i18n("Do full sync on startup") + onToggled: SettingsManager.refreshOnStartup = checked + } + + MobileForm.FormCheckDelegate { + enabled: Sync.syncEnabled + checked: SettingsManager.syncWhenUpdatingFeeds + text: i18n("Do full sync when fetching podcasts") + onToggled: SettingsManager.syncWhenUpdatingFeeds = checked + } + + MobileForm.FormCheckDelegate { + enabled: Sync.syncEnabled + checked: SettingsManager.syncWhenPlayerstateChanges + text: i18n("Upload episode play positions on play/pause toggle") + onToggled: SettingsManager.syncWhenPlayerstateChanges = checked + } + } + } + + MobileForm.FormCard { + Layout.topMargin: Kirigami.Units.largeSpacing + Layout.fillWidth: true + + contentItem: ColumnLayout { + spacing: 0 + + MobileForm.FormCardHeader { + title: i18n("Advanced options") + } + + MobileForm.FormTextDelegate { + id: fetchAllEpisodeStates + text: i18n("Fetch all episode states from server") + + trailing: Controls.Button { + text: i18n("Fetch") + enabled: Sync.syncEnabled + onClicked: { + forceSyncFeedsAndEpisodes.run(); + } + } + } + + MobileForm.FormDelegateSeparator { above: fetchAllEpisodeStates; below: fetchLocalEpisodeStates } + + MobileForm.FormTextDelegate { + id: fetchLocalEpisodeStates + text: i18n("Fetch all local episode states from server") + + trailing: Controls.Button { + enabled: Sync.syncEnabled + text: i18n("Push") + onClicked: { + syncPushAllStatesDialog.open(); } } } } } + } - Kirigami.Dialog { - id: syncLoginOverlay - preferredWidth: Kirigami.Units.gridUnit * 25 - padding: Kirigami.Units.largeSpacing + // This item can be used to trigger an update of all feeds; it will open an + // overlay with options in case the operation is not allowed by the settings + ConnectionCheckAction { + id: syncFeedsAndEpisodes - showCloseButton: true - standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel - closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside + function action() { + Sync.doRegularSync(); + } + } - title: i18n("Sync Login Credentials") + // This item can be used to trigger an update of all feeds; it will open an + // overlay with options in case the operation is not allowed by the settings + ConnectionCheckAction { + id: forceSyncFeedsAndEpisodes - onAccepted: { - if (Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked) { - Sync.hostname = hostnameField.text; - } else { - Sync.hostname = "" - } - Sync.login(usernameField.text, passwordField.text); - syncLoginOverlay.close(); + function action() { + Sync.doForceSync(); + } + } + + Kirigami.Dialog { + id: syncPushAllStatesDialog + preferredWidth: Kirigami.Units.gridUnit * 25 + padding: Kirigami.Units.largeSpacing + + showCloseButton: true + standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel + closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside + + title: i18n("Push all local episode states to server?") + + onAccepted: { + syncPushAllStatesDialog.close(); + syncPushAllStates.run(); + } + onRejected: syncPushAllStatesDialog.close(); + + RowLayout { + spacing: Kirigami.Units.largeSpacing + Kirigami.Icon { + Layout.preferredHeight: Kirigami.Units.gridUnit * 4 + Layout.preferredWidth: Kirigami.Units.gridUnit * 4 + source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" } - onRejected: syncLoginOverlay.close(); - - Column { - spacing: Kirigami.Units.largeSpacing - RowLayout { - width: parent.width - spacing: Kirigami.Units.largeSpacing - Kirigami.Icon { - Layout.preferredHeight: Kirigami.Units.gridUnit * 4 - Layout.preferredWidth: Kirigami.Units.gridUnit * 4 - source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" - } - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - Kirigami.Heading { - clip: true - level: 2 - text: Sync.provider === Sync.GPodderNextcloud ? i18n("Sync with GPodder Nextcloud app") : i18n("Sync with gpodder.net service") - } - TextEdit { - Layout.fillWidth: true - readOnly: true - wrapMode: Text.WordWrap - textFormat: Text.RichText - onLinkActivated: Qt.openUrlExternally(link) - text: Sync.provider === Sync.GPodderNextcloud ? - i18nc("argument is a weblink", "Sync with a Nextcloud server that has the GPodder Sync app installed: %1.
It is advised to manually create an app password for Kasts through the web interface and use those credentials." , "https://apps.nextcloud.com/apps/gpoddersync") : - i18nc("argument is a weblink", "If you don't already have an account, you should first create one at %1", "https://gpodder.net") - color: Kirigami.Theme.textColor - } - } - } - GridLayout { - width: parent.width - columns: 2 - rowSpacing: Kirigami.Units.smallSpacing - columnSpacing: Kirigami.Units.smallSpacing - Controls.Label { - Layout.alignment: Qt.AlignRight - text: i18n("Username:") - } - Controls.TextField { - id: usernameField - Layout.fillWidth: true - text: Sync.username - Keys.onReturnPressed: syncLoginOverlay.accepted(); - // focus: syncLoginOverlay.visible // disabled for now since it causes problem with virtual keyboard appearing at the same time as the overlay - } - Controls.Label { - Layout.alignment: Qt.AlignRight - text: i18n("Password:") - } - Controls.TextField { - id: passwordField - Layout.fillWidth: true - echoMode: TextInput.Password - text: Sync.password - Keys.onReturnPressed: syncLoginOverlay.accepted(); - } - Controls.CheckBox { - id: customServerCheckBox - Layout.row: 2 - Layout.column: 1 - visible: Sync.provider === Sync.GPodderNet - checked: false - text: i18n("Use custom server") - } - Controls.Label { - visible: Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked - Layout.alignment: Qt.AlignRight - text: i18n("Hostname:") - } - Controls.TextField { - visible: Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked - id: hostnameField - Layout.fillWidth: true - placeholderText: Sync.provider === Sync.GPodderNet ? "https://gpodder.net" : "https://nextcloud.mydomain.org" - text: Sync.hostname - Keys.onReturnPressed: syncLoginOverlay.accepted(); - } - } + TextEdit { + Layout.fillWidth: true + Layout.fillHeight: true + readOnly: true + wrapMode: Text.WordWrap + 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?") + color: Kirigami.Theme.textColor + Keys.onReturnPressed: accepted(); } } + } - Connections { - target: Sync - function onDeviceListReceived() { - syncDeviceOverlay.open(); - syncDeviceOverlay.update(); - } - function onLoginSucceeded() { - if (Sync.provider === Sync.GPodderNextcloud) { - firstSyncOverlay.open(); - } - } + // This item can be used to trigger a push of all episode states to the server; + // it will open an overlay with options in case the operation is not allowed by the settings + ConnectionCheckAction { + id: syncPushAllStates + + function action() { + Sync.doSyncPushAll(); } + } - Kirigami.Dialog { - id: syncDeviceOverlay - preferredWidth: Kirigami.Units.gridUnit * 25 - padding: Kirigami.Units.largeSpacing + Kirigami.Dialog { + id: syncProviderOverlay + preferredWidth: Kirigami.Units.gridUnit * 20 + standardButtons: Kirigami.Dialog.NoButton - showCloseButton: true + showCloseButton: true - title: i18n("Sync Device Settings") + title: i18n("Select Sync Provider") - Column { - spacing: Kirigami.Units.largeSpacing * 2 - Kirigami.Heading { - level: 2 - text: i18n("Create a new device") + ColumnLayout { + spacing: 0 + + Repeater { + focus: syncProviderOverlay.visible + + model: ListModel { + id: providerModel } - GridLayout { - columns: 2 - width: parent.width - Controls.Label { - text: i18n("Device Name:") - } - Controls.TextField { - id: deviceField - Layout.fillWidth: true - text: Sync.suggestedDevice - Keys.onReturnPressed: createDeviceButton.clicked(); - // focus: syncDeviceOverlay.visible // disabled for now since it causes problem with virtual keyboard appearing at the same time as the overlay - } - Controls.Label { - text: i18n("Device Description:") - } - Controls.TextField { - id: deviceNameField - Layout.fillWidth: true - text: Sync.suggestedDeviceName - Keys.onReturnPressed: createDeviceButton.clicked(); - } - Controls.Label { - text: i18n("Device Type:") - } - Controls.ComboBox { - id: deviceTypeField - textRole: "text" - valueRole: "value" - popup.z: 102 // popup has to go in front of OverlaySheet - model: [{"text": i18n("other"), "value": "other"}, - {"text": i18n("desktop"), "value": "desktop"}, - {"text": i18n("laptop"), "value": "laptop"}, - {"text": i18n("server"), "value": "server"}, - {"text": i18n("mobile"), "value": "mobile"}] - } + Component.onCompleted: { + providerModel.append({"name": i18n("gpodder.net"), + "subtitle": i18n("Synchronize with official gpodder.net server"), + "icon": "gpodder", + "provider": Sync.GPodderNet}); + providerModel.append({"name": i18n("GPodder Nextcloud"), + "subtitle": i18n("Synchronize with GPodder Nextcloud app"), + "icon": "kaccounts-nextcloud", + "provider": Sync.GPodderNextcloud}); } - Controls.Button { - id: createDeviceButton - text: i18n("Create Device") - icon.name: "list-add" + delegate: Kirigami.BasicListItem { + Layout.fillWidth: true + label: model.name + subtitle: model.subtitle + icon: model.icon + //highlighted: false + iconSize: Kirigami.Units.gridUnit * 3 + Keys.onReturnPressed: clicked() onClicked: { - Sync.registerNewDevice(deviceField.text, deviceNameField.text, deviceTypeField.currentValue); - syncDeviceOverlay.close(); + Sync.provider = model.provider; + syncProviderOverlay.close(); + syncLoginOverlay.open(); } } - ListView { - id: deviceList - width: parent.width - height: contentItem.childrenRect.height - visible: deviceListModel.count !== 0 + } + } + } - header: Kirigami.Heading { - topPadding: Kirigami.Units.gridUnit - bottomPadding: Kirigami.Units.largeSpacing + Kirigami.Dialog { + id: syncLoginOverlay + preferredWidth: Kirigami.Units.gridUnit * 25 + padding: Kirigami.Units.largeSpacing + + showCloseButton: true + standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel + closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside + + title: i18n("Sync Login Credentials") + + onAccepted: { + if (Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked) { + Sync.hostname = hostnameField.text; + } else { + Sync.hostname = "" + } + Sync.login(usernameField.text, passwordField.text); + syncLoginOverlay.close(); + } + onRejected: syncLoginOverlay.close(); + + Column { + spacing: Kirigami.Units.largeSpacing + RowLayout { + width: parent.width + spacing: Kirigami.Units.largeSpacing + Kirigami.Icon { + Layout.preferredHeight: Kirigami.Units.gridUnit * 4 + Layout.preferredWidth: Kirigami.Units.gridUnit * 4 + source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" + } + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Kirigami.Heading { + clip: true level: 2 - text: i18n("or select an existing device") + text: Sync.provider === Sync.GPodderNextcloud ? i18n("Sync with GPodder Nextcloud app") : i18n("Sync with gpodder.net service") } - model: ListModel { - id: deviceListModel - } - - delegate: Kirigami.BasicListItem { - label: model.device.caption - highlighted: false - icon: model.device.type == "desktop" ? "computer" : - model.device.type == "laptop" ? "computer-laptop" : - model.device.type == "server" ? "network-server-database" : - model.device.type == "mobile" ? "smartphone" : - "emblem-music-symbolic" - onClicked: { - syncDeviceOverlay.close(); - Sync.device = model.device.id; - Sync.deviceName = model.device.caption; - Sync.syncEnabled = true; - syncGroupOverlay.open(); - } + TextEdit { + Layout.fillWidth: true + readOnly: true + wrapMode: Text.WordWrap + textFormat: Text.RichText + onLinkActivated: Qt.openUrlExternally(link) + text: Sync.provider === Sync.GPodderNextcloud ? + i18nc("argument is a weblink", "Sync with a Nextcloud server that has the GPodder Sync app installed: %1.
It is advised to manually create an app password for Kasts through the web interface and use those credentials." , "https://apps.nextcloud.com/apps/gpoddersync") : + i18nc("argument is a weblink", "If you don't already have an account, you should first create one at %1", "https://gpodder.net") + color: Kirigami.Theme.textColor } } } - - function update() { - deviceListModel.clear(); - for (var index in Sync.deviceList) { - deviceListModel.append({"device": Sync.deviceList[index]}); + GridLayout { + width: parent.width + columns: 2 + rowSpacing: Kirigami.Units.smallSpacing + columnSpacing: Kirigami.Units.smallSpacing + Controls.Label { + Layout.alignment: Qt.AlignRight + text: i18n("Username:") } - } - } - - Connections { - target: Sync - function onDeviceCreated() { - syncGroupOverlay.open(); - } - } - - Kirigami.Dialog { - id: syncGroupOverlay - preferredWidth: Kirigami.Units.gridUnit * 25 - padding: Kirigami.Units.largeSpacing - - showCloseButton: true - standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel - closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside - - title: i18n("Device Sync Settings") - - onAccepted: { - Sync.linkUpAllDevices(); - syncGroupOverlay.close(); - } - onRejected: { - syncGroupOverlay.close(); - } - - RowLayout { - spacing: Kirigami.Units.largeSpacing - Kirigami.Icon { - Layout.preferredHeight: Kirigami.Units.gridUnit * 4 - Layout.preferredWidth: Kirigami.Units.gridUnit * 4 - source: "gpodder" - } - TextEdit { + Controls.TextField { + id: usernameField Layout.fillWidth: true - Layout.fillHeight: true - readOnly: true - wrapMode: Text.WordWrap - 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\".") - color: Kirigami.Theme.textColor - Keys.onReturnPressed: accepted(); + text: Sync.username + Keys.onReturnPressed: syncLoginOverlay.accepted(); + // focus: syncLoginOverlay.visible // disabled for now since it causes problem with virtual keyboard appearing at the same time as the overlay } - } - - onVisibleChanged: { - if (!visible) { - firstSyncOverlay.open(); + Controls.Label { + Layout.alignment: Qt.AlignRight + text: i18n("Password:") } - } - } - - Kirigami.Dialog { - id: firstSyncOverlay - preferredWidth: Kirigami.Units.gridUnit * 16 - padding: Kirigami.Units.largeSpacing - - showCloseButton: true - standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel - closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside - - title: i18n("Sync Now?") - - onAccepted: { - firstSyncOverlay.close(); - Sync.doRegularSync(); - } - onRejected: firstSyncOverlay.close(); - - RowLayout { - spacing: Kirigami.Units.largeSpacing - Kirigami.Icon { - Layout.preferredHeight: Kirigami.Units.gridUnit * 4 - Layout.preferredWidth: Kirigami.Units.gridUnit * 4 - source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" - } - TextEdit { + Controls.TextField { + id: passwordField Layout.fillWidth: true - Layout.fillHeight: true - readOnly: true - wrapMode: Text.WordWrap - text: i18n("Perform a first sync now?") - color: Kirigami.Theme.textColor - Keys.onReturnPressed: accepted(); + echoMode: TextInput.Password + text: Sync.password + Keys.onReturnPressed: syncLoginOverlay.accepted(); } - } - } - - Controls.Button { - text: i18n("Logout") - enabled: Sync.syncEnabled - onClicked: { - Sync.logout(); - } - } - - Controls.CheckBox { - Kirigami.FormData.label: i18n("Automatic Syncing:") - enabled: Sync.syncEnabled - checked: SettingsManager.refreshOnStartup - text: i18n("Do full sync on startup") - onToggled: SettingsManager.refreshOnStartup = checked - } - - Controls.CheckBox { - enabled: Sync.syncEnabled - checked: SettingsManager.syncWhenUpdatingFeeds - text: i18n("Do full sync when fetching podcasts") - onToggled: SettingsManager.syncWhenUpdatingFeeds = checked - } - - Controls.CheckBox { - enabled: Sync.syncEnabled - checked: SettingsManager.syncWhenPlayerstateChanges - text: i18n("Upload episode play positions on play/pause toggle") - onToggled: SettingsManager.syncWhenPlayerstateChanges = checked - } - - Controls.Button { - Kirigami.FormData.label: i18n("Manual Syncing:") - text: i18n("Sync Now") - enabled: Sync.syncEnabled - onClicked: { - syncFeedsAndEpisodes.run(); - } - } - - // This item can be used to trigger an update of all feeds; it will open an - // overlay with options in case the operation is not allowed by the settings - ConnectionCheckAction { - id: syncFeedsAndEpisodes - - function action() { - Sync.doRegularSync(); - } - } - - Kirigami.Heading { - Kirigami.FormData.isSection: true - text: i18n("Advanced Options") - } - - Controls.Button { - text: i18n("Fetch all episode states from server") - enabled: Sync.syncEnabled - onClicked: { - forceSyncFeedsAndEpisodes.run(); - } - } - - // This item can be used to trigger an update of all feeds; it will open an - // overlay with options in case the operation is not allowed by the settings - ConnectionCheckAction { - id: forceSyncFeedsAndEpisodes - - function action() { - Sync.doForceSync(); - } - } - - Controls.Button { - enabled: Sync.syncEnabled - text: i18n("Push all local episode states to server") - onClicked: { - syncPushAllStatesDialog.open(); - } - } - - Kirigami.Dialog { - id: syncPushAllStatesDialog - preferredWidth: Kirigami.Units.gridUnit * 25 - padding: Kirigami.Units.largeSpacing - - showCloseButton: true - standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel - closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside - - title: i18n("Push all local episode states to server?") - - onAccepted: { - syncPushAllStatesDialog.close(); - syncPushAllStates.run(); - } - onRejected: syncPushAllStatesDialog.close(); - - RowLayout { - spacing: Kirigami.Units.largeSpacing - Kirigami.Icon { - Layout.preferredHeight: Kirigami.Units.gridUnit * 4 - Layout.preferredWidth: Kirigami.Units.gridUnit * 4 - source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" + Controls.CheckBox { + id: customServerCheckBox + Layout.row: 2 + Layout.column: 1 + visible: Sync.provider === Sync.GPodderNet + checked: false + text: i18n("Use custom server") } - TextEdit { + Controls.Label { + visible: Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked + Layout.alignment: Qt.AlignRight + text: i18n("Hostname:") + } + Controls.TextField { + visible: Sync.provider === Sync.GPodderNextcloud || customServerCheckBox.checked + id: hostnameField Layout.fillWidth: true - Layout.fillHeight: true - readOnly: true - wrapMode: Text.WordWrap - 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?") - color: Kirigami.Theme.textColor - Keys.onReturnPressed: accepted(); + placeholderText: Sync.provider === Sync.GPodderNet ? "https://gpodder.net" : "https://nextcloud.mydomain.org" + text: Sync.hostname + Keys.onReturnPressed: syncLoginOverlay.accepted(); + } + } + } + } + + Connections { + target: Sync + function onDeviceListReceived() { + syncDeviceOverlay.open(); + syncDeviceOverlay.update(); + } + function onLoginSucceeded() { + if (Sync.provider === Sync.GPodderNextcloud) { + firstSyncOverlay.open(); + } + } + } + + Kirigami.Dialog { + id: syncDeviceOverlay + preferredWidth: Kirigami.Units.gridUnit * 25 + padding: Kirigami.Units.largeSpacing + + showCloseButton: true + + title: i18n("Sync Device Settings") + + Column { + spacing: Kirigami.Units.largeSpacing * 2 + Kirigami.Heading { + level: 2 + text: i18n("Create a new device") + } + GridLayout { + columns: 2 + width: parent.width + Controls.Label { + text: i18n("Device Name:") + } + Controls.TextField { + id: deviceField + Layout.fillWidth: true + text: Sync.suggestedDevice + Keys.onReturnPressed: createDeviceButton.clicked(); + // focus: syncDeviceOverlay.visible // disabled for now since it causes problem with virtual keyboard appearing at the same time as the overlay + } + Controls.Label { + text: i18n("Device Description:") + } + Controls.TextField { + id: deviceNameField + Layout.fillWidth: true + text: Sync.suggestedDeviceName + Keys.onReturnPressed: createDeviceButton.clicked(); + } + Controls.Label { + text: i18n("Device Type:") + } + Controls.ComboBox { + id: deviceTypeField + textRole: "text" + valueRole: "value" + popup.z: 102 // popup has to go in front of OverlaySheet + model: [{"text": i18n("other"), "value": "other"}, + {"text": i18n("desktop"), "value": "desktop"}, + {"text": i18n("laptop"), "value": "laptop"}, + {"text": i18n("server"), "value": "server"}, + {"text": i18n("mobile"), "value": "mobile"}] + } + } + Controls.Button { + id: createDeviceButton + text: i18n("Create Device") + icon.name: "list-add" + onClicked: { + Sync.registerNewDevice(deviceField.text, deviceNameField.text, deviceTypeField.currentValue); + syncDeviceOverlay.close(); + } + } + ListView { + id: deviceList + width: parent.width + height: contentItem.childrenRect.height + visible: deviceListModel.count !== 0 + + header: Kirigami.Heading { + topPadding: Kirigami.Units.gridUnit + bottomPadding: Kirigami.Units.largeSpacing + level: 2 + text: i18n("or select an existing device") + } + model: ListModel { + id: deviceListModel + } + + delegate: Kirigami.BasicListItem { + label: model.device.caption + highlighted: false + icon: model.device.type == "desktop" ? "computer" : + model.device.type == "laptop" ? "computer-laptop" : + model.device.type == "server" ? "network-server-database" : + model.device.type == "mobile" ? "smartphone" : + "emblem-music-symbolic" + onClicked: { + syncDeviceOverlay.close(); + Sync.device = model.device.id; + Sync.deviceName = model.device.caption; + Sync.syncEnabled = true; + syncGroupOverlay.open(); + } } } } - // This item can be used to trigger a push of all episode states to the server; - // it will open an overlay with options in case the operation is not allowed by the settings - ConnectionCheckAction { - id: syncPushAllStates + function update() { + deviceListModel.clear(); + for (var index in Sync.deviceList) { + deviceListModel.append({"device": Sync.deviceList[index]}); + } + } + } - function action() { - Sync.doSyncPushAll(); + Connections { + target: Sync + function onDeviceCreated() { + syncGroupOverlay.open(); + } + } + + Kirigami.Dialog { + id: syncGroupOverlay + preferredWidth: Kirigami.Units.gridUnit * 25 + padding: Kirigami.Units.largeSpacing + + showCloseButton: true + standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel + closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside + + title: i18n("Device Sync Settings") + + onAccepted: { + Sync.linkUpAllDevices(); + syncGroupOverlay.close(); + } + onRejected: { + syncGroupOverlay.close(); + } + + RowLayout { + spacing: Kirigami.Units.largeSpacing + Kirigami.Icon { + Layout.preferredHeight: Kirigami.Units.gridUnit * 4 + Layout.preferredWidth: Kirigami.Units.gridUnit * 4 + source: "gpodder" + } + TextEdit { + Layout.fillWidth: true + Layout.fillHeight: true + readOnly: true + wrapMode: Text.WordWrap + 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\".") + color: Kirigami.Theme.textColor + Keys.onReturnPressed: accepted(); } } + onVisibleChanged: { + if (!visible) { + firstSyncOverlay.open(); + } + } + } + + Kirigami.Dialog { + id: firstSyncOverlay + preferredWidth: Kirigami.Units.gridUnit * 16 + padding: Kirigami.Units.largeSpacing + + showCloseButton: true + standardButtons: Controls.DialogButtonBox.Ok | Controls.DialogButtonBox.Cancel + closePolicy: Kirigami.Dialog.CloseOnEscape | Kirigami.Dialog.CloseOnPressOutside + + title: i18n("Sync Now?") + + onAccepted: { + firstSyncOverlay.close(); + Sync.doRegularSync(); + } + onRejected: firstSyncOverlay.close(); + + RowLayout { + spacing: Kirigami.Units.largeSpacing + Kirigami.Icon { + Layout.preferredHeight: Kirigami.Units.gridUnit * 4 + Layout.preferredWidth: Kirigami.Units.gridUnit * 4 + source: Sync.provider === Sync.GPodderNextcloud ? "kaccounts-nextcloud" : "gpodder" + } + TextEdit { + Layout.fillWidth: true + Layout.fillHeight: true + readOnly: true + wrapMode: Text.WordWrap + text: i18n("Perform a first sync now?") + color: Kirigami.Theme.textColor + Keys.onReturnPressed: accepted(); + } + } } }