Skip to content

Commit 77fcf57

Browse files
committed
Allow managing pending devs/dirs in Qt Quick GUI
1 parent efc2fbf commit 77fcf57

13 files changed

+332
-18
lines changed

tray/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ if (QUICK_GUI)
330330
gui/qml/ObjectConfigDelegate.qml
331331
gui/qml/ObjectConfigPage.qml
332332
gui/qml/OutOfSyncDirs.qml
333+
gui/qml/PendingDevices.qml
334+
gui/qml/PendingDirs.qml
333335
gui/qml/StartPage.qml
334336
gui/qml/Statistics.qml
335337
gui/qml/StatisticsPage.qml

tray/gui/qml/AdvancedDevConfigPage.qml

+6
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@ AdvancedConfigPage {
1212
required property string devId
1313
function makeNewConfig() {
1414
const config = App.connection.rawConfig?.defaults?.device ?? {};
15+
16+
// add device ID and name as default values for deviceID/name
1517
if (devId.length > 0) {
1618
config.deviceID = devId;
1719
}
20+
if (devName.length > 0) {
21+
config.name = devName;
22+
}
23+
1824
isNew = true;
1925
return config;
2026
}

tray/gui/qml/AdvancedPage.qml

+16-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ StackView {
1919
anchors.fill: parent
2020
model: ListModel {
2121
id: model
22+
ListElement {
23+
specialPage: "PendingDevices.qml"
24+
label: qsTr("Pending devices")
25+
}
26+
ListElement {
27+
specialPage: "PendingDirs.qml"
28+
label: qsTr("Pending folders")
29+
}
2230
ListElement {
2331
key: "remoteIgnoredDevices"
2432
label: qsTr("Ignored devices")
@@ -56,7 +64,13 @@ StackView {
5664
delegate: ItemDelegate {
5765
width: listView.width
5866
text: label
59-
onClicked: stackView.push("ObjectConfigPage.qml", {title: title, isDangerous: isDangerous, configObject: advancedPage.config[key], path: key, configCategory: `config-option-${key}`, itemLabel: itemLabel, helpUrl: helpUrl, stackView: stackView}, StackView.PushTransition)
67+
onClicked: {
68+
if (specialPage.length > 0) {
69+
stackView.push(specialPage, {pages: stackView.pages}, StackView.PushTransition)
70+
} else {
71+
stackView.push("ObjectConfigPage.qml", {title: title, isDangerous: isDangerous, configObject: advancedPage.config[key], path: key, configCategory: `config-option-${key}`, itemLabel: itemLabel, helpUrl: helpUrl, stackView: stackView}, StackView.PushTransition)
72+
}
73+
}
6074
}
6175
}
6276

@@ -89,4 +103,5 @@ StackView {
89103
}
90104
]
91105
}
106+
required property var pages
92107
}

tray/gui/qml/DevConfigPage.qml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import QtQuick
22

33
AdvancedDevConfigPage {
44
id: devConfigPage
5-
title: qsTr("Config of device \"%1\"").arg(devName)
5+
title: devName.length > 0 ? qsTr("Config of device \"%1\"").arg(devName) : qsTr("Add new device")
66
isDangerous: false
77
specialEntriesOnly: true
88
specialEntries: [

tray/gui/qml/DevsPage.qml

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ StackView {
3838
}
3939
]
4040
property alias model: devsListView.mainModel
41-
function add(deviceId = "") {
42-
stackView.push("DevConfigPage.qml", {title: qsTr("Add new device"), devName: qsTr("New device"), devId: deviceId, stackView: stackView}, StackView.PushTransition);
41+
function add(deviceId = "", deviceName = "") {
42+
stackView.push("DevConfigPage.qml", {devId: deviceId, devName: deviceName, stackView: stackView}, StackView.PushTransition);
4343
}
4444
}
4545
}

tray/gui/qml/DirConfigPage.qml

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Main
44

55
AdvancedDirConfigPage {
66
id: dirConfigPage
7-
title: qsTr("Config of folder \"%1\"").arg(dirName)
7+
title: dirName.length > 0 ? qsTr("Config of folder \"%1\"").arg(dirName) : qsTr("Add new folder")
88
isDangerous: false
99
specialEntriesOnly: true
1010
specialEntries: [
@@ -18,7 +18,7 @@ AdvancedDirConfigPage {
1818
{value: "receiveonly", label: "Receive Only", desc: qsTr("Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.")},
1919
{value: "receiveencrypted", label: "Receive Encrypted", desc: qsTr("Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"Receive Encrypted\" too. Can only be assigned to new folders.")},
2020
]},
21-
{key: "devices", type: "devices", label: qsTr("Share with"), desc: qsTr("Select devices to share this folder with.")},
21+
{key: "devices", type: "devices", label: qsTr("Share with"), desc: qsTr("Select devices to share this folder with."), selectIds: dirConfigPage.shareWithDeviceIds},
2222
{key: "versioning", label: qsTr("Versioning"), desc: qsTr("Syncthing supports archiving the old version of a file when it is deleted or replaced with a newer version from the cluster. Versioning applies to changes received from <i>other</i> devices."), helpUrl: "https://docs.syncthing.net/users/versioning"},
2323
{key: "fsWatcherEnabled", label: qsTr("Watch for Changes"), desc: qsTr("Use notifications from the filesystem to detect changed items. Watching for changes discovers most changes without periodic scanning."), helpUrl: "https://docs.syncthing.net/users/syncing#scanning"},
2424
{key: "rescanIntervalS", label: qsTr("Rescan Interval"), desc: qsTr("The frequency in which Syncthing will rescan the folder for changes. Can be set to 0 to rely on triggering rescans manually.")},
@@ -75,5 +75,5 @@ AdvancedDirConfigPage {
7575
]},
7676
],
7777
})
78-
property string shareWithDeviceId
78+
property list<string> shareWithDeviceIds
7979
}

tray/gui/qml/DirsPage.qml

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ StackView {
4343
}
4444
]
4545
property alias model: dirsListView.mainModel
46-
function add(dirId = "", dirName = "", shareWithDeviceId = "") {
47-
stackView.push("DirConfigPage.qml", {title: qsTr("Add new folder"), dirName: qsTr("New folder"), dirId: dirId, dirName: dirName, shareWithDeviceId: shareWithDeviceId, stackView: stackView}, StackView.PushTransition);
46+
function add(dirId = "", dirName = "", shareWithDeviceIds = []) {
47+
stackView.push("DirConfigPage.qml", {dirId: dirId, dirName: dirName, shareWithDeviceIds: shareWithDeviceIds ?? [], stackView: stackView}, StackView.PushTransition);
4848
}
4949
}
5050
}

tray/gui/qml/Main.qml

+6-5
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ ApplicationWindow {
335335
}
336336
AdvancedPage {
337337
id: advancedPage
338+
pages: pageStack
338339
}
339340
SettingsPage {
340341
id: settingsPage
@@ -386,13 +387,13 @@ ApplicationWindow {
386387
pageStack.setCurrentIndex(indexForward.pop());
387388
return true;
388389
}
389-
function addDir(dirId, dirName, shareWithDeviceId) {
390+
function addDir(dirId, dirName, shareWithDeviceIds) {
390391
pageStack.setCurrentIndex(1);
391-
pageStack.currentPage.add(dirId, dirName, shareWithDeviceId);
392+
pageStack.currentPage.add(dirId, dirName, shareWithDeviceIds);
392393
}
393-
function addDevice(deviceId) {
394+
function addDevice(deviceId, deviceName) {
394395
pageStack.setCurrentIndex(2);
395-
pageStack.currentPage.add(deviceId);
396+
pageStack.currentPage.add(deviceId, deviceName);
396397
}
397398
}
398399
CustomDialog {
@@ -495,7 +496,7 @@ ApplicationWindow {
495496
pageStack.addDevice(devId);
496497
}
497498
function onNewDirTriggered(devId, dirId, dirLabel) {
498-
pageStack.addDir(dirId, dirLabel, devId);
499+
pageStack.addDir(dirId, dirLabel, [devId]);
499500
}
500501
}
501502
Connections {

tray/gui/qml/ObjectConfigDelegate.qml

+8
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,16 @@ DelegateChooser {
379379
required property var modelData
380380
property var devicesModel: {
381381
const devices = [];
382+
const idsToSelect = modelData.selectIds;
382383
const sharedDevices = objectConfigPage.configObject[modelData.key];
383384
const myId = App.connection.myId;
385+
if (typeof idsToSelect === "object") {
386+
for (const idToSelect of idsToSelect) {
387+
if (idToSelect !== myId) {
388+
sharedDevices.push({deviceID: idToSelect, encryptionPassword: "", introducedBy: ""});
389+
}
390+
}
391+
}
384392
for (const sharedDev of sharedDevices) {
385393
if (sharedDev.deviceID !== myId) {
386394
devices.push(sharedDev);

tray/gui/qml/PendingDevices.qml

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import QtQuick
2+
import QtQuick.Layouts
3+
import QtQuick.Controls.Material
4+
5+
import Main
6+
7+
Page {
8+
id: page
9+
title: qsTr("Pending devices")
10+
Component.onCompleted: page.load()
11+
actions: [
12+
Action {
13+
text: qsTr("Refresh")
14+
icon.source: App.faUrlBase + "refresh"
15+
onTriggered: (source) => page.load()
16+
}
17+
]
18+
contentItem: CustomListView {
19+
id: listView
20+
delegate: ItemDelegate {
21+
id: itemDelegate
22+
width: listView.width
23+
contentItem: ColumnLayout {
24+
width: listView.width
25+
Label {
26+
Layout.fillWidth: true
27+
text: App.deviceDisplayName(modelData.devId)
28+
elide: Text.ElideRight
29+
font.weight: Font.Medium
30+
wrapMode: Text.WordWrap
31+
}
32+
Label {
33+
Layout.fillWidth: true
34+
text: qsTr("Name: ") + (modelData.info.Name ?? modelData.info.name)
35+
elide: Text.ElideRight
36+
font.weight: Font.Light
37+
wrapMode: Text.WordWrap
38+
}
39+
Label {
40+
Layout.fillWidth: true
41+
text: qsTr("Address: ") + (modelData.info.Address ?? modelData.info.address)
42+
elide: Text.ElideRight
43+
font.weight: Font.Light
44+
wrapMode: Text.WordWrap
45+
}
46+
RowLayout {
47+
Layout.fillWidth: true
48+
Button {
49+
text: qsTr("Ignore")
50+
flat: true
51+
onClicked: page.ignoreDev(modelData.devId)
52+
}
53+
Button {
54+
text: qsTr("Add device")
55+
flat: true
56+
onClicked: pages.addDevice(modelData.devId, modelData.info.Name ?? modelData.info.name)
57+
}
58+
}
59+
}
60+
required property var modelData
61+
}
62+
model: ListModel {
63+
id: listModel
64+
}
65+
}
66+
67+
required property var pages
68+
required property list<Action> actions
69+
70+
function load() {
71+
App.requestFromSyncthing("GET", "cluster/pending/devices", {}, (res, error) => {
72+
listModel.clear();
73+
if (error.length > 0) {
74+
return;
75+
}
76+
Object.entries(res).forEach((pendingDev) => {
77+
listModel.append({devId: pendingDev[0], info: pendingDev[1]});
78+
});
79+
});
80+
}
81+
82+
function ignoreDev(devId, info) {
83+
const cfg = App.connection.rawConfig;
84+
const ignoredDevs = cfg.remoteIgnoredDevices;
85+
if (!Array.isArray(ignoredDevs)) {
86+
return false;
87+
}
88+
ignoredDevs.push({deviceID: devId, name: info.Name ?? info.name ?? "", address: info.Address ?? info.address ?? "", time: new Date().toISOString()});
89+
App.postSyncthingConfig(cfg, (error) => {
90+
if (error.length === 0) {
91+
page.load();
92+
}
93+
});
94+
return true;
95+
}
96+
}

0 commit comments

Comments
 (0)