lib/config: Rewrite pending notifications (fixes #2291)

This commit is contained in:
Audrius Butkevicius
2018-08-25 11:36:10 +01:00
committed by GitHub
parent 03c0537340
commit aec66045ef
15 changed files with 630 additions and 230 deletions

View File

@@ -28,9 +28,7 @@ angular.module('syncthing.core')
$scope.model = {};
$scope.myID = '';
$scope.devices = [];
$scope.deviceRejections = {};
$scope.discoveryCache = {};
$scope.folderRejections = {};
$scope.protocolChanged = false;
$scope.reportData = {};
$scope.reportDataPreview = '';
@@ -260,14 +258,6 @@ angular.module('syncthing.core')
}
});
$scope.$on(Events.DEVICE_REJECTED, function (event, arg) {
$scope.deviceRejections[arg.data.device] = arg;
});
$scope.$on(Events.FOLDER_REJECTED, function (event, arg) {
$scope.folderRejections[arg.data.folder + "-" + arg.data.device] = arg;
});
$scope.$on(Events.CONFIG_SAVED, function (event, arg) {
updateLocalConfig(arg.data);
@@ -967,6 +957,7 @@ angular.module('syncthing.core')
// loop through all devices
var deviceCount = $scope.devices.length;
var pendingFolders = 0;
for (var i = 0; i < $scope.devices.length; i++) {
var status = $scope.deviceStatus({
deviceID:$scope.devices[i].deviceID
@@ -982,10 +973,13 @@ angular.module('syncthing.core')
deviceCount--;
break;
}
pendingFolders += $scope.devices[i].pendingFolders.length;
}
// enumerate notifications
if ($scope.openNoAuth || !$scope.configInSync || $scope.sizeOf($scope.deviceRejections) > 0 || $scope.sizeOf($scope.folderRejections) > 0 || $scope.errorList().length > 0 || !online) {
if ($scope.openNoAuth || !$scope.configInSync || $scope.errorList().length > 0 || !online || (
!isEmptyObject($scope.config) && ($scope.config.pendingDevices.length > 0 || pendingFolders > 0)
)) {
notifyCount++;
}
@@ -1025,6 +1019,14 @@ angular.module('syncthing.core')
return matches[0].name;
};
$scope.friendlyNameFromID = function (deviceID) {
var match = $scope.findDevice(deviceID);
if (match) {
return $scope.deviceName(match);
}
return deviceID.substr(0, 6);
};
$scope.findDevice = function (deviceID) {
var matches = $scope.devices.filter(function (n) {
return n.deviceID === deviceID;
@@ -1178,8 +1180,24 @@ angular.module('syncthing.core')
$scope.tmpOptions.upgrades = "candidate";
}
$scope.tmpGUI = angular.copy($scope.config.gui);
$('#settings').modal().one('hidden.bs.modal', function () {
$scope.tmpRemoteIgnoredDevices = angular.copy($scope.config.remoteIgnoredDevices);
$scope.tmpDevices = angular.copy($scope.config.devices);
var settingsModal = $('#settings').modal();
settingsModal.one('hidden.bs.modal', function () {
$('.nav-tabs a[href="#settings-general"]').tab('show');
window.location.hash = "";
settingsModal.off('hide.bs.modal');
}).on('hide.bs.modal', function (e) {
if ($scope.settingsModified()) {
$("#discard-changes-confirmation").modal().one('hidden.bs.modal', function() {
if (!$scope.settingsModified()) {
settingsModal.modal('hide');
}
});
e.preventDefault();
e.stopImmediatePropagation();
return false;
}
});
};
@@ -1210,11 +1228,30 @@ angular.module('syncthing.core')
return result;
};
$scope.settingsModified = function () {
// Options has artificial properties injected into the temp config.
// Need to recompute them before we can check equality
var options = angular.copy($scope.config.options);
options.deviceName = $scope.thisDevice().name;
options.upgrades = "none";
if (options.autoUpgradeIntervalH > 0) {
options.upgrades = "stable";
}
if (options.upgradeToPreReleases) {
options.upgrades = "candidate";
}
var optionsEqual = angular.equals(options, $scope.tmpOptions);
var guiEquals = angular.equals($scope.config.gui, $scope.tmpGUI);
var ignoredDevicesEquals = angular.equals($scope.config.remoteIgnoredDevices, $scope.tmpRemoteIgnoredDevices);
var ignoredFoldersEquals = angular.equals($scope.config.devices, $scope.tmpDevices);
console.log("settings equals - options: " + optionsEqual + " gui: " + guiEquals + " ignDev: " + ignoredDevicesEquals + " ignFol: " + ignoredFoldersEquals);
return !optionsEqual || !guiEquals || !ignoredDevicesEquals || !ignoredFoldersEquals;
}
$scope.saveSettings = function () {
// Make sure something changed
var changed = !angular.equals($scope.config.options, $scope.tmpOptions) || !angular.equals($scope.config.gui, $scope.tmpGUI);
var themeChanged = $scope.config.gui.theme !== $scope.tmpGUI.theme;
if (changed) {
if ($scope.settingsModified()) {
var themeChanged = $scope.config.gui.theme !== $scope.tmpGUI.theme;
// Angular has issues with selects with numeric values, so we handle strings here.
$scope.tmpOptions.urAccepted = parseInt($scope.tmpOptions._urAcceptedStr);
// Check if auto-upgrade has been enabled or disabled. This
@@ -1241,6 +1278,8 @@ angular.module('syncthing.core')
$scope.thisDevice().name = $scope.tmpOptions.deviceName;
$scope.config.options = angular.copy($scope.tmpOptions);
$scope.config.gui = angular.copy($scope.tmpGUI);
$scope.config.remoteIgnoredDevices = angular.copy($scope.tmpRemoteIgnoredDevices);
$scope.config.devices = angular.copy($scope.tmpDevices);
['listenAddresses', 'globalAnnounceServers'].forEach(function (key) {
$scope.config.options[key] = $scope.config.options["_" + key + "Str"].split(/[ ,]+/).map(function (x) {
@@ -1367,11 +1406,6 @@ angular.module('syncthing.core')
return n.deviceID !== $scope.currentDevice.deviceID;
});
$scope.config.devices = $scope.devices;
// In case we later added the device manually, remove the ignoral
// record.
$scope.config.ignoredDevices = $scope.config.ignoredDevices.filter(function (id) {
return id !== $scope.currentDevice.deviceID;
});
for (var id in $scope.folders) {
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
@@ -1385,7 +1419,6 @@ angular.module('syncthing.core')
$scope.saveDevice = function () {
$('#editDevice').modal('hide');
$scope.saveDeviceConfig($scope.currentDevice);
$scope.dismissDeviceRejection($scope.currentDevice.deviceID);
};
$scope.saveDeviceConfig = function (deviceCfg) {
@@ -1407,11 +1440,6 @@ angular.module('syncthing.core')
$scope.devices.sort(deviceCompare);
$scope.config.devices = $scope.devices;
// In case we are adding the device manually, remove the ignoral
// record.
$scope.config.ignoredDevices = $scope.config.ignoredDevices.filter(function (id) {
return id !== deviceCfg.deviceID;
});
for (var id in deviceCfg.selectedFolders) {
if (deviceCfg.selectedFolders[id]) {
@@ -1438,14 +1466,37 @@ angular.module('syncthing.core')
$scope.saveConfig();
};
$scope.dismissDeviceRejection = function (device) {
delete $scope.deviceRejections[device];
$scope.ignoreDevice = function (pendingDevice) {
pendingDevice = angular.copy(pendingDevice);
// Bump time
pendingDevice.time = (new Date()).toISOString();
$scope.config.remoteIgnoredDevices.push(pendingDevice);
$scope.saveConfig();
};
$scope.ignoreRejectedDevice = function (device) {
$scope.config.ignoredDevices.push(device);
$scope.saveConfig();
$scope.dismissDeviceRejection(device);
$scope.unignoreDeviceFromTemporaryConfig = function (ignoredDevice) {
$scope.tmpRemoteIgnoredDevices = $scope.tmpRemoteIgnoredDevices.filter(function (existingIgnoredDevice) {
return ignoredDevice.deviceID !== existingIgnoredDevice.deviceID;
});
};
$scope.ignoredFoldersCountTmpConfig = function () {
var count = 0;
($scope.tmpDevices || []).forEach(function (deviceCfg) {
count += deviceCfg.ignoredFolders.length;
});
return count;
};
$scope.unignoreFolderFromTemporaryConfig = function (device, ignoredFolderID) {
for (var i = 0; i < $scope.tmpDevices.length; i++) {
if ($scope.tmpDevices[i].deviceID == device) {
$scope.tmpDevices[i].ignoredFolders = $scope.tmpDevices[i].ignoredFolders.filter(function (existingIgnoredFolder) {
return existingIgnoredFolder.id !== ignoredFolderID;
});
return;
}
}
};
$scope.otherDevices = function () {
@@ -1565,6 +1616,7 @@ angular.module('syncthing.core')
$('#folder-ignores textarea').focus();
}
}).one('hidden.bs.modal', function () {
$('.nav-tabs a[href="#folder-general"]').tab('show');
window.location.hash = "";
});
};
@@ -1641,7 +1693,6 @@ angular.module('syncthing.core')
};
$scope.addFolderAndShare = function (folder, folderLabel, device) {
$scope.dismissFolderRejection(folder, device);
$scope.editingExisting = false;
$scope.currentFolder = angular.copy($scope.folderDefaults);
$scope.currentFolder.id = folder;
@@ -1661,7 +1712,6 @@ angular.module('syncthing.core')
});
$scope.config.folders = folderList($scope.folders);
$scope.saveConfig();
$scope.dismissFolderRejection(folder, device);
};
$scope.saveFolder = function () {
@@ -1749,14 +1799,18 @@ angular.module('syncthing.core')
});
};
$scope.dismissFolderRejection = function (folder, device) {
delete $scope.folderRejections[folder + "-" + device];
};
$scope.ignoreFolder = function (device, pendingFolder) {
pendingFolder = angular.copy(pendingFolder);
// Bump time
pendingFolder.time = (new Date()).toISOString();
$scope.ignoreRejectedFolder = function (folder, device) {
$scope.config.ignoredFolders.push(folder);
$scope.saveConfig();
$scope.dismissFolderRejection(folder, device);
for (var i = 0; i < $scope.devices.length; i++) {
if ($scope.devices[i].deviceID == device) {
$scope.devices[i].ignoredFolders.push(pendingFolder);
$scope.saveConfig();
return;
}
}
};
$scope.sharesFolder = function (folderCfg) {
@@ -1788,7 +1842,7 @@ angular.module('syncthing.core')
return folderID;
}
var label = $scope.folders[folderID].label;
return label.length > 0 ? label : folderID;
return label && label.length > 0 ? label : folderID;
}
$scope.deleteFolder = function (id) {

View File

@@ -1,7 +1,7 @@
<modal id="editDevice" status="default" icon="{{editingExisting ? 'fas fa-pencil-alt' : 'fas fa-desktop'}}" heading="{{editingExisting ? 'Edit Device' : 'Add Device' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<form role="form" name="deviceEditor">
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(folderEditor)">
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(deviceEditor)">
<li class="active"><a data-toggle="tab" href="#device-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
<li><a data-toggle="tab" href="#device-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
<li><a data-toggle="tab" href="#device-advanced"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>

View File

@@ -28,8 +28,8 @@
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>
</button>
</div>
</modal>

View File

@@ -0,0 +1,16 @@
<modal id="discard-changes-confirmation" status="warning" icon="fas fa-question-circle" heading="{{'Pending changes' | translate}}" large="no" closeable="yes">
<div class="modal-body">
<p style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
<span translate>You have unsaved changes. Do you really want to discard them?</span>
</p>
</div>
<div class="modal-footer">
<!-- editSettings() repopulates temp variables, which then makes the settingsModified() check pass -->
<button type="button" class="btn btn-warning pull-left btn-sm" data-dismiss="modal" ng-click="editSettings()">
<span class="fas fa-check"></span>&nbsp;<span translate>Discard</span>
</button>
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Take me back</span>
</button>
</div>
</modal>

View File

@@ -5,6 +5,24 @@
<li class="active"><a data-toggle="tab" href="#settings-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
<li><a data-toggle="tab" href="#settings-gui"><span class="fas fa-desktop"></span> <span translate>GUI</span></a></li>
<li><a data-toggle="tab" href="#settings-connections"><span class="fas fa-sitemap"></span> <span translate>Connections</span></a></li>
<li>
<a data-toggle="tab" href="#settings-ignored-devices">
<span class="fas fa-laptop"></span>
&nbsp;
<span translate>Ignored Devices</span>
&nbsp;
<span class="badge">{{ tmpRemoteIgnoredDevices.length }}</span>
</a>
</li>
<li>
<a data-toggle="tab" href="#settings-ignored-folders">
<span class="fas fa-folder"></span>
&nbsp;
<span translate>Ignored Folders</span>
&nbsp;
<span class="badge">{{ ignoredFoldersCountTmpConfig() }}</span>
</a>
</li>
</ul>
<div class="tab-content">
<div id="settings-general" class="tab-pane in active">
@@ -228,6 +246,72 @@
</div>
</div>
<div id="settings-ignored-devices" class="tab-pane">
<div class="form-group">
<span ng-if="tmpRemoteIgnoredDevices.length === 0" translate>You have no ignored devices.</span>
<div class="table-responsive" ng-if="tmpRemoteIgnoredDevices.length > 0">
<table class="table-condensed table-striped table" style="table-layout: auto;">
<thead>
<tr>
<th translate>Ignored at</th>
<th translate>Device</th>
<th translate>Address</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ignoredDevice in tmpRemoteIgnoredDevices">
<td class="no-overflow-ellipse">{{ ignoredDevice.time | date:"yyyy-MM-dd HH:mm:ss" }}</td>
<td>
<span ng-if="!ignoredDevice.name">{{ ignoredDevice.deviceID }}</span>
<span tooltip data-original-title="{{ ignoredDevice.deviceID }}" ng-if="ignoredDevice.name">{{ ignoredDevice.name }}</span>
</td>
<td class="no-overflow-ellipse">{{ ignoredDevice.address }}</td>
<td>
<a href="" ng-click="unignoreDeviceFromTemporaryConfig(ignoredDevice)">
<span class="fas fa-times"></span>&nbsp;<span translate>Unignore</span>
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="settings-ignored-folders" class="tab-pane">
<div class="form-group">
<span ng-if="ignoredFoldersCountTmpConfig() === 0" translate>You have no ignored folders.</span>
<div class="table-responsive" ng-if="ignoredFoldersCountTmpConfig() > 0">
<table class="table-condensed table-striped table" style="table-layout: auto;">
<thead>
<tr>
<th translate>Ignored at</th>
<th translate>Folder</th>
<th translate>Device</th>
<th></th>
</tr>
</thead>
<tbody ng-repeat-start="device in tmpDevices">
<tr ng-repeat="ignoredFolder in device.ignoredFolders">
<td class="no-overflow-ellipse">{{ ignoredFolder.time | date:"yyyy-MM-dd HH:mm:ss" }}</td>
<td>{{ folderLabel(ignoredFolder.id) }}</td>
<td>
<span tooltip data-original-title="{{ device.deviceID }}">{{ friendlyNameFromID(device.deviceID) }}</span>
</td>
<td>
<a href="" ng-click="unignoreFolderFromTemporaryConfig(device.deviceID, ignoredFolder.id)">
<span class="fas fa-times"></span>&nbsp;<span translate>Unignore</span>
</a>
</td>
</tr>
</tbody>
<tfoot ng-repeat-end></tfoot>
</table>
</div>
</div>
</div>
</div>
</form>
</div>