File: SettingsPage.qml

package info (click to toggle)
syncthingtray 2.0.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,124 kB
  • sloc: cpp: 34,081; xml: 1,705; java: 1,258; sh: 97; javascript: 54; makefile: 25
file content (254 lines) | stat: -rw-r--r-- 14,961 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Material
import QtQuick.Dialogs

import Main

StackView {
    id: stackView
    Layout.fillWidth: true
    Layout.fillHeight: true
    initialItem: Page {
        id: appSettingsPage
        title: qsTr("App settings")
        Layout.fillWidth: true
        Layout.fillHeight: true

        CustomListView {
            id: listView
            anchors.fill: parent
            model: ListModel {
                id: model
                ListElement {
                    key: "connection"
                    label: qsTr("Connection to Syncthing backend")
                    title: qsTr("Configure connection with the Syncthing backend")
                    category: qsTr("Configuration")
                    iconName: "link"
                }
                ListElement {
                    key: "launcher"
                    label: qsTr("Execution of Syncthing backend")
                    title: qsTr("Configure how to run the Syncthing backend")
                    category: qsTr("Configuration")
                    iconName: "terminal"
                }
                ListElement {
                    key: "tweaks"
                    label: qsTr("Tweaks")
                    title: qsTr("Configure details of the app's behavior")
                    category: qsTr("Configuration")
                    iconName: "cogs"
                }
                ListElement {
                    callback: () => stackView.push("ErrorsPage.qml", {}, StackView.PushTransition)
                    label: qsTr("Syncthing notifications/errors")
                    category: qsTr("Diagnostics")
                    iconName: "exclamation-triangle"
                }
                ListElement {
                    callback: () => stackView.push("InternalErrorsPage.qml", {}, StackView.PushTransition)
                    label: qsTr("Log of Syncthing API errors")
                    category: qsTr("Diagnostics")
                    iconName: "exclamation-circle"
                }
                ListElement {
                    callback: () => stackView.push("StatisticsPage.qml", {stackView: stackView}, StackView.PushTransition)
                    label: qsTr("Statistics")
                    category: qsTr("Diagnostics")
                    iconName: "area-chart"
                }
                ListElement {
                    label: qsTr("Save support bundle")
                    functionName: "saveSupportBundle"
                    category: qsTr("Diagnostics")
                    iconName: "user-md"
                }
                ListElement {
                    functionName: "checkSettings"
                    callback: (availableSettings) => stackView.push("ImportPage.qml", {availableSettings: availableSettings}, StackView.PushTransition)
                    label: qsTr("Import selected settings/secrets/data of app and backend")
                    category: qsTr("Backup")
                    iconName: "download"
                }
                ListElement {
                    functionName: "exportSettings"
                    label: qsTr("Export all settings/secrets/data of app and backend")
                    category: qsTr("Backup")
                    iconName: "floppy-o"
                }
                ListElement {
                    callback: () => stackView.push("HomeDirPage.qml", {}, StackView.PushTransition)
                    label: qsTr("Move Syncthing home directory")
                    category: qsTr("Maintenance actions")
                    iconName: "folder-open-o"
                }
                ListElement {
                    callback: () => App.cleanSyncthingHomeDirectory()
                    label: qsTr("Clean Syncthing home directory")
                    title: qsTr("Removes the migrated database of Syncthing v1")
                    category: qsTr("Maintenance actions")
                    iconName: "eraser"
                }
                ListElement {
                    callback: () => deleteLogFileDialog.open()
                    label: qsTr("Clear log file")
                    title: qsTr("Disables persistent logging and removes the log file")
                    category: qsTr("Maintenance actions")
                    iconName: "trash-o"
                }
            }
            delegate: CustomDelegate {
                width: listView.width
                labelText: modelData.label
                iconName: modelData.iconName
                onClicked: {
                    if (modelData.key.length > 0) {
                        appSettingsPage.openNestedSettings(modelData.title, modelData.key);
                    } else if (modelData.functionName.length > 0) {
                        appSettingsPage.initiateBackup(modelData.functionName, modelData.callback);
                    } else if (modelData.callback !== undefined) {
                        modelData.callback();
                    }
                }
                required property var modelData
            }
            section.property: "category"
            section.delegate: SectionHeader {
            }
        }

        FileDialog {
            id: backupFileDialog
            fileMode: appSettingsPage.currentBackupFunction === "exportSettings" || appSettingsPage.currentBackupFunction === "saveSupportBundle"
                      ? FileDialog.SaveFile : FileDialog.OpenFile
            onAccepted: App[appSettingsPage.currentBackupFunction](backupFileDialog.selectedFile, appSettingsPage.currentBackupCallback)
        }

        FolderDialog {
            id: backupFolderDialog
            onAccepted: App[appSettingsPage.currentBackupFunction](backupFolderDialog.selectedFolder, appSettingsPage.currentBackupCallback)
        }

        CustomDialog {
            id: deleteLogFileDialog
            title: meta.title
            contentItem: Label {
                Layout.fillWidth: true
                text: qsTr("Do you really want to delete the persistent log file?")
                wrapMode: Text.WordWrap
            }
            onAccepted: App.clearLogfile()
        }

        property alias listView: listView
        property alias backupFileDialog: backupFileDialog
        property alias backupFolderDialog: backupFolderDialog
        property string currentBackupFunction
        property var currentBackupCallback
        function initiateBackup(functionName, callback) {
            const tweaks = App.settings.tweaks;
            appSettingsPage.currentBackupFunction = functionName;
            appSettingsPage.currentBackupCallback = callback;
            if (tweaks.exportDir?.length > 0 && (functionName === "exportSettings" || functionName === "saveSupportBundle")) {
                return App[functionName]("", callback);
            }
            return tweaks.importExportAsArchive || functionName === "saveSupportBundle" ? backupFileDialog.open() : backupFolderDialog.open();
        }
        function openNestedSettings(title, key) {
            if (appSettingsPage.config[key] === undefined) {
                appSettingsPage.config[key] = {};
            }
            stackView.push("ObjectConfigPage.qml", {
                               title: title,
                               parentPage: appSettingsPage,
                               configObject: Qt.binding(() => appSettingsPage.config[key]),
                               specialEntries: appSettingsPage.specialEntries[key] ?? [],
                               specialEntriesByKey: appSettingsPage.specialEntries,
                               specialEntriesOnly: true,
                               stackView: stackView,
                               actions: appSettingsPage.actions},
                           StackView.PushTransition)
        }

        property var config: App.settings
        readonly property var specialEntries: ({
            connection: [
                {key: "useLauncher", type: "boolean", label: qsTr("Automatic"), statusText: qsTr("Connect to the Syncthing backend launched via this app and disregard the manual settings below."), category: qsTr("General")},
                {key: "pauseOnMeteredConnection", type: "boolean", defaultValue: false, label: qsTr("Pause devices, discovery and relaying on metered network connection"), statusText: Qt.binding(() => App.meteredStatus)},
                {key: "advanced", label: qsTr("Advanced")},
                {key: "syncthingUrl", label: qsTr("Syncthing URL"), category: qsTr("Manual connection settings")},
                {key: "apiKey", label: qsTr("API key"), inputMethodHints: Qt.ImhHiddenText | Qt.ImhSensitiveData | Qt.ImhNoAutoUppercase},
                {key: "httpsCertPath", label: qsTr("HTTPs certificate path"), type: "filepath"},
                {key: "httpAuth", label: qsTr("HTTP authentication")},
            ],
            advanced: [
                {key: "requestTimeout", label: qsTr("Transfer timeout"), desc: qsTr("The timeout for normal requests via the REST-API in milliseconds. Set to 0 for no limit."), helpUrl: "", category: qsTr("Timeouts")},
                {key: "longPollingTimeout", label: qsTr("Long polling timeout/interval"), desc: qsTr("The timeout for event API requests using long polling in milliseconds. Set to 0 to use the default limit of Syncthing."), helpUrl: ""},
                {key: "trafficPollInterval", label: qsTr("Poll interval for traffic"), desc: qsTr("The poll interval for traffic statistics in milliseconds."), helpUrl: "", category: qsTr("Polling")},
                {key: "devStatsPollInterval", label: qsTr("Poll interval for device statistics"), desc: qsTr("The poll interval for device statistics in milliseconds."), helpUrl: ""},
                {key: "errorsPollInterval", label: qsTr("Poll interval for errors"), desc: qsTr("The poll interval for errors in milliseconds."), helpUrl: ""},
                {key: "reconnectInterval", label: qsTr("Re-connect interval"), desc: qsTr("The interval for re-connect attempts in milliseconds."), helpUrl: ""},
                {key: "diskEventLimit", label: qsTr("Limit for recent changes"), desc: qsTr("The maximum number of recent changes to query/buffer."), helpUrl: "", category: qsTr("Miscellaneous")},
                {key: "localPath", type: "filepath", label: qsTr("Local path"), desc: qsTr("The path to the Unix domain socket when setting the Syncthing URL to \"unix+http://…\"."), helpUrl: ""},
                {key: "specialEntriesOnly", value: true},
            ],
            httpAuth: [
                {key: "enabled", label: qsTr("Enabled")},
                {key: "userName", label: qsTr("Username")},
                {key: "password", label: qsTr("Password"), inputMethodHints: Qt.ImhHiddenText | Qt.ImhSensitiveData | Qt.ImhNoAutoUppercase},
            ],
            launcher: [
                {key: "run", label: qsTr("Run Syncthing"), statusText: Qt.binding(() => App.syncthingRunningStatus), category: qsTr("General")},
                {key: "stopOnMetered", label: qsTr("Stop on metered network connection"), statusText: Qt.binding(() => App.meteredStatus)},
                {key: "guiUrl", type: "readonly", label: qsTr("URL for GUI and API access"), defaultValue: "", statusText: Qt.binding(() => App.syncthingGuiUrl.toString() || qsTr("n/a"))},
                {key: "writeLogFile", label: qsTr("Write persistent log file"), statusText: qsTr("Write a persistent log file into the app directory"), category: qsTr("Logging")},
                {key: "logLevel", label: qsTr("Log level"), type: "options", options: [
                    {value: "debug", label: qsTr("Debug")},
                    {value: "info", label: qsTr("Info")},
                    {value: "warning", label: qsTr("Warning")},
                    {value: "error", label: qsTr("Error")},
                ]},
                {key: "openLogs", label: qsTr("Open logs"), statusText: qsTr("Show Syncthing logs since app startup"), defaultValue: () => stackView.push("LogPage.qml", {}, StackView.PushTransition)},
                {key: "openPersistentLogs", label: qsTr("Open persistent logs"), statusText: qsTr("Open persistent log file externally"), defaultValue: () => App.openSyncthingLogFile()},
                {key: "exePath", type: "filepath", label: qsTr("External executable"), helpUrl: "", desc: qsTr("Start an external executable instead of using the built-in version of Syncthing. When empty, the built-in version of Syncthing is used."), category: qsTr("Advanced")},
            ],
            tweaks: [
                {key: "importExportAsArchive", type: "boolean", defaultValue: false, label: qsTr("Import/export archive"), statusText: qsTr("Import and export to/from a Zip archive"), category: qsTr("Import/export")},
                {key: "importExportEncryptionPassword", type: "string", inputMethodHints: Qt.ImhHiddenText | Qt.ImhSensitiveData | Qt.ImhNoAutoUppercase, defaultValue: "", label: qsTr("Import/export password"), statusText: qsTr("Encrypt/decrypt data via AES-256 when exporting/importing to archive")},
                {key: "exportDir", type: "folderpath", defaultValue: "", label: qsTr("Export path"), statusText: qsTr("Save exports and support bundles under fix location")},
                {key: "useUnixDomainSocket", type: "boolean", defaultValue: false, label: qsTr("Use Unix domain socket"), statusText: qsTr("Reduces communication overhead and makes Syncthing API and web GUI inaccessible to other apps, applied after restart"), category: qsTr("Backend")},
                {key: "closePreference", label: qsTr("Close preference"), type: "options", category: qsTr("Interface"), options: [
                    {value: "", label: qsTr("Ask")},
                    {value: "background", label: qsTr("Keep Syncthing in background")},
                    {value: "shutdown", label: qsTr("Shut Syncthing down")},
                ]},
            ]
        })
        property bool hasUnsavedChanges: false
        property list<Action> actions: [
            Action {
                text: qsTr("Apply")
                icon.source: App.faUrlBase + "check"
                onTriggered: {
                    const cfg = App.settings;
                    for (let i = 0, count = model.count; i !== count; ++i) {
                        const entryKey = model.get(i).key;
                        if (entryKey.length > 0) {
                            cfg[entryKey] = appSettingsPage.config[entryKey];
                        }
                    }
                    App.settings = cfg;
                    for (let i = 0, count = depth; i !== count; ++i) {
                        const item = stackView.get(i);
                        if (item.hasUnsavedChanges) {
                            item.hasUnsavedChanges = false;
                        }
                    }
                    return true;
                }
            }
        ]
    }
}