mirror of
https://github.com/octoleo/syncthing.git
synced 2025-02-02 11:58:28 +00:00
This commit is contained in:
parent
bca6d31b95
commit
0c46e0a9cc
@ -378,6 +378,7 @@
|
|||||||
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
|
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
|
||||||
"The path cannot be blank.": "The path cannot be blank.",
|
"The path cannot be blank.": "The path cannot be blank.",
|
||||||
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
|
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
|
||||||
|
"The remote device has not accepted sharing this folder.": "The remote device has not accepted sharing this folder.",
|
||||||
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
|
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
|
||||||
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
|
"There are no devices to share this folder with.": "There are no devices to share this folder with.",
|
||||||
"There are no file versions to restore.": "There are no file versions to restore.",
|
"There are no file versions to restore.": "There are no file versions to restore.",
|
||||||
|
@ -532,7 +532,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th><span class="fas fa-fw fa-share-alt"></span> <span translate>Shared With</span></th>
|
<th><span class="fas fa-fw fa-share-alt"></span> <span translate>Shared With</span></th>
|
||||||
<td class="text-right" ng-attr-title="{{sharesFolder(folder)}}">{{sharesFolder(folder)}}</td>
|
<td class="text-right">
|
||||||
|
<span tooltip data-original-title="{{sharesFolder(folder)}} {{folderHasUnacceptedDevices(folder) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : ''}}" ng-bind-html="sharesFolder(folder)"></span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="folderStats[folder.id].lastScan">
|
<tr ng-if="folderStats[folder.id].lastScan">
|
||||||
<th><span class="far fa-fw fa-clock"></span> <span translate>Last Scan</span></th>
|
<th><span class="far fa-fw fa-clock"></span> <span translate>Last Scan</span></th>
|
||||||
@ -668,8 +670,8 @@
|
|||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<span class="data" tooltip data-original-title="{{'Show detailed listener status' | translate}}.">
|
<span class="data" tooltip data-original-title="{{'Show detailed listener status' | translate}}.">
|
||||||
<a href="" ng-class="{'text-success': listenersFailed.length == 0, 'text-danger': listenersFailed.length == listenersTotal}" ng-click="showListenerStatus()">
|
<a href="" ng-class="{'text-success': listenersFailed.length == 0, 'text-danger': listenersFailed.length == listenersTotal}" ng-click="showListenerStatus()">
|
||||||
{{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
|
{{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -678,8 +680,8 @@
|
|||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<span class="data" tooltip data-original-title="{{'Show detailed discovery status' | translate}}.">
|
<span class="data" tooltip data-original-title="{{'Show detailed discovery status' | translate}}.">
|
||||||
<a href="" ng-class="{'text-success': discoveryFailed.length == 0, 'text-danger': discoveryFailed.length == discoveryTotal}" ng-click="showDiscoveryStatus()">
|
<a href="" ng-class="{'text-success': discoveryFailed.length == 0, 'text-danger': discoveryFailed.length == discoveryTotal}" ng-click="showDiscoveryStatus()">
|
||||||
{{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}
|
{{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -833,7 +835,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="deviceFolders(deviceCfg).length > 0">
|
<tr ng-if="deviceFolders(deviceCfg).length > 0">
|
||||||
<th><span class="fas fa-fw fa-folder"></span> <span translate>Folders</span></th>
|
<th><span class="fas fa-fw fa-folder"></span> <span translate>Folders</span></th>
|
||||||
<td class="text-right" ng-attr-title="{{deviceFolders(deviceCfg).map(folderLabel).join(', ')}}">{{deviceFolders(deviceCfg).map(folderLabel).join(", ")}}</td>
|
<td class="text-right">
|
||||||
|
<span tooltip data-original-title="{{sharedFolders(deviceCfg)}} {{deviceHasUnacceptedFolders(deviceCfg) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : '' }}" ng-bind-html="sharedFolders(deviceCfg)"></span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="deviceCfg.remoteGUIPort > 0">
|
<tr ng-if="deviceCfg.remoteGUIPort > 0">
|
||||||
<th><span class="fas fa-fw fa-desktop"></span> <span translate>Remote GUI</span></th>
|
<th><span class="fas fa-fw fa-desktop"></span> <span translate>Remote GUI</span></th>
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<div class="col-md-6 checkbox">
|
<div class="col-md-6 checkbox">
|
||||||
<label for="sharedwith-{{id}}">
|
<label for="sharedwith-{{id}}">
|
||||||
<input id="sharedwith-{{id}}" ng-model="selected[id]" type="checkbox" />
|
<input id="sharedwith-{{id}}" ng-model="selected[id]" type="checkbox" />
|
||||||
<span tooltip data-original-title="{{id}}">{{label}}</span>
|
<span tooltip data-original-title="{{id}}" ng-bind-html="label"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-addon" ng-switch="folderType !== 'receiveencrypted' && !encryptionPasswords[id]">
|
<span class="input-group-addon" ng-switch="folderType !== 'receiveencrypted' && !encryptionPasswords[id]">
|
||||||
<span ng-switch-when='true' class="fas fa-fw fa-unlock" />
|
<span ng-switch-when='true' class="fas fa-fw fa-unlock"></span>
|
||||||
<span ng-switch-default class="fas fa-fw fa-lock" />
|
<span ng-switch-default class="fas fa-fw fa-lock"></span>
|
||||||
</span>
|
</span>
|
||||||
<span ng-switch="folderType === 'receiveencrypted'">
|
<span ng-switch="folderType === 'receiveencrypted'">
|
||||||
<span ng-switch-when='true'>
|
<span ng-switch-when='true'>
|
||||||
@ -30,10 +30,10 @@
|
|||||||
</span>
|
</span>
|
||||||
<span ng-switch="selected[id] && folderType !== 'receiveencrypted'" class="input-group-addon">
|
<span ng-switch="selected[id] && folderType !== 'receiveencrypted'" class="input-group-addon">
|
||||||
<span ng-switch-when='true'>
|
<span ng-switch-when='true'>
|
||||||
<span class="button fas fa-fw fa-eye" ng-click="togglePasswordVisibility()" />
|
<span class="button fas fa-fw fa-eye" ng-click="togglePasswordVisibility()"></span>
|
||||||
</span>
|
</span>
|
||||||
<span ng-switch-default>
|
<span ng-switch-default>
|
||||||
<span class="button fas fa-fw fa-eye" disabled />
|
<span class="button fas fa-fw fa-eye" disabled></span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2365,17 +2365,37 @@ angular.module('syncthing.core')
|
|||||||
+ '&device=' + encodeURIComponent(deviceID));
|
+ '&device=' + encodeURIComponent(deviceID));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.deviceNameMarkUnaccepted = function (deviceID, folderID) {
|
||||||
|
var name = $scope.deviceName($scope.devices[deviceID]);
|
||||||
|
// Add footnote if sharing was not accepted on the remote device
|
||||||
|
if (deviceID in $scope.completion && folderID in $scope.completion[deviceID] && !$scope.completion[deviceID][folderID].accepted) {
|
||||||
|
name += '<sup>1</sup>';
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.sharesFolder = function (folderCfg) {
|
$scope.sharesFolder = function (folderCfg) {
|
||||||
var names = [];
|
var names = [];
|
||||||
folderCfg.devices.forEach(function (device) {
|
folderCfg.devices.forEach(function (device) {
|
||||||
if (device.deviceID !== $scope.myID) {
|
if (device.deviceID !== $scope.myID) {
|
||||||
names.push($scope.deviceName($scope.devices[device.deviceID]));
|
names.push($scope.deviceNameMarkUnaccepted(device.deviceID, folderCfg.id));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
names.sort();
|
names.sort();
|
||||||
return names.join(", ");
|
return names.join(", ");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.folderHasUnacceptedDevices = function (folderCfg) {
|
||||||
|
for (var deviceID in $scope.completion) {
|
||||||
|
if (deviceID in $scope.devices
|
||||||
|
&& folderCfg.id in $scope.completion[deviceID]
|
||||||
|
&& !$scope.completion[deviceID][folderCfg.id].accepted) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.deviceFolders = function (deviceCfg) {
|
$scope.deviceFolders = function (deviceCfg) {
|
||||||
var folders = [];
|
var folders = [];
|
||||||
$scope.folderList().forEach(function (folder) {
|
$scope.folderList().forEach(function (folder) {
|
||||||
@ -2397,6 +2417,36 @@ angular.module('syncthing.core')
|
|||||||
return label && label.length > 0 ? label : folderID;
|
return label && label.length > 0 ? label : folderID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.folderLabelMarkUnaccepted = function (folderID, deviceID) {
|
||||||
|
var label = $scope.folderLabel(folderID);
|
||||||
|
// Add footnote if sharing was not accepted on the remote device
|
||||||
|
if (deviceID in $scope.completion && folderID in $scope.completion[deviceID] && !$scope.completion[deviceID][folderID].accepted) {
|
||||||
|
label += '<sup>1</sup>';
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.sharedFolders = function (deviceCfg) {
|
||||||
|
var labels = [];
|
||||||
|
$scope.deviceFolders(deviceCfg).forEach(function (folderID) {
|
||||||
|
labels.push($scope.folderLabelMarkUnaccepted(folderID, deviceCfg.deviceID));
|
||||||
|
});
|
||||||
|
return labels.join(', ');
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deviceHasUnacceptedFolders = function (deviceCfg) {
|
||||||
|
if (!(deviceCfg.deviceID in $scope.completion)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var folderID in $scope.completion[deviceCfg.deviceID]) {
|
||||||
|
if (folderID in $scope.folders
|
||||||
|
&& !$scope.completion[deviceCfg.deviceID][folderID].accepted) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.deleteFolder = function (id) {
|
$scope.deleteFolder = function (id) {
|
||||||
hideFolderModal();
|
hideFolderModal();
|
||||||
if ($scope.currentFolder._editing != "existing") {
|
if ($scope.currentFolder._editing != "existing") {
|
||||||
|
@ -83,8 +83,11 @@
|
|||||||
<a href="#" ng-click="selectAllSharedFolders(false)" translate>Deselect All</a></small>
|
<a href="#" ng-click="selectAllSharedFolders(false)" translate>Deselect All</a></small>
|
||||||
</p>
|
</p>
|
||||||
<div class="form-group" ng-repeat="folder in currentSharing.shared">
|
<div class="form-group" ng-repeat="folder in currentSharing.shared">
|
||||||
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{folder.id}}" label="{{folderLabel(folder.id)}}" folder-type="{{folder.type}}" untrusted="currentDevice.untrusted" />
|
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{folder.id}}" label="{{folderLabelMarkUnaccepted(folder.id, currentDevice.deviceID)}}" folder-type="{{folder.type}}" untrusted="currentDevice.untrusted" />
|
||||||
</div>
|
</div>
|
||||||
|
<p class="help-block" ng-if="deviceHasUnacceptedFolders(currentDevice)">
|
||||||
|
<sup>1</sup> <span translate>The remote device has not accepted sharing this folder.</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-horizontal" ng-if="currentSharing.unrelated.length">
|
<div class="form-horizontal" ng-if="currentSharing.unrelated.length">
|
||||||
<label translate for="folders">Unshared Folders</label>
|
<label translate for="folders">Unshared Folders</label>
|
||||||
|
@ -56,8 +56,11 @@
|
|||||||
<a href="#" ng-click="selectAllSharedDevices(false)" translate>Deselect All</a></small>
|
<a href="#" ng-click="selectAllSharedDevices(false)" translate>Deselect All</a></small>
|
||||||
</p>
|
</p>
|
||||||
<div class="form-group" ng-repeat="device in currentSharing.shared">
|
<div class="form-group" ng-repeat="device in currentSharing.shared">
|
||||||
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{device.deviceID}}" label="{{deviceName(device)}}" folder-type="{{currentFolder.type}}" untrusted="device.untrusted || pendingIsRemoteEncrypted(currentFolder.id, device.deviceID)" />
|
<share-template selected="currentSharing.selected" encryption-passwords="currentSharing.encryptionPasswords" id="{{device.deviceID}}" label="{{deviceNameMarkUnaccepted(device.deviceID, currentFolder.id)}}" folder-type="{{currentFolder.type}}" untrusted="device.untrusted || pendingIsRemoteEncrypted(currentFolder.id, device.deviceID)" />
|
||||||
</div>
|
</div>
|
||||||
|
<p class="help-block" ng-if="folderHasUnacceptedDevices(currentFolder)">
|
||||||
|
<sup>1</sup> <span translate>The remote device has not accepted sharing this folder.</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-horizontal" ng-if="currentSharing.unrelated.length || otherDevices().length <= 0">
|
<div class="form-horizontal" ng-if="currentSharing.unrelated.length || otherDevices().length <= 0">
|
||||||
<label translate>Unshared Devices</label>
|
<label translate>Unshared Devices</label>
|
||||||
|
@ -52,6 +52,14 @@ func (s folderState) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type remoteFolderState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
remoteNotSharing remoteFolderState = iota
|
||||||
|
remotePaused
|
||||||
|
remoteValid
|
||||||
|
)
|
||||||
|
|
||||||
type stateTracker struct {
|
type stateTracker struct {
|
||||||
folderID string
|
folderID string
|
||||||
evLogger events.Logger
|
evLogger events.Logger
|
||||||
|
@ -471,7 +471,7 @@ func (r *indexHandlerRegistry) Remove(folder string) {
|
|||||||
// RemoveAllExcept stops all running index handlers and removes those pending to be started,
|
// RemoveAllExcept stops all running index handlers and removes those pending to be started,
|
||||||
// except mentioned ones.
|
// except mentioned ones.
|
||||||
// It is a noop if the folder isn't known.
|
// It is a noop if the folder isn't known.
|
||||||
func (r *indexHandlerRegistry) RemoveAllExcept(except map[string]struct{}) {
|
func (r *indexHandlerRegistry) RemoveAllExcept(except map[string]remoteFolderState) {
|
||||||
r.mut.Lock()
|
r.mut.Lock()
|
||||||
defer r.mut.Unlock()
|
defer r.mut.Unlock()
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ type model struct {
|
|||||||
closed map[protocol.DeviceID]chan struct{}
|
closed map[protocol.DeviceID]chan struct{}
|
||||||
helloMessages map[protocol.DeviceID]protocol.Hello
|
helloMessages map[protocol.DeviceID]protocol.Hello
|
||||||
deviceDownloads map[protocol.DeviceID]*deviceDownloadState
|
deviceDownloads map[protocol.DeviceID]*deviceDownloadState
|
||||||
remotePausedFolders map[protocol.DeviceID]map[string]struct{} // deviceID -> folders
|
remoteFolderStates map[protocol.DeviceID]map[string]remoteFolderState // deviceID -> folders
|
||||||
indexHandlers map[protocol.DeviceID]*indexHandlerRegistry
|
indexHandlers map[protocol.DeviceID]*indexHandlerRegistry
|
||||||
|
|
||||||
// for testing only
|
// for testing only
|
||||||
@ -246,7 +246,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
|
|||||||
closed: make(map[protocol.DeviceID]chan struct{}),
|
closed: make(map[protocol.DeviceID]chan struct{}),
|
||||||
helloMessages: make(map[protocol.DeviceID]protocol.Hello),
|
helloMessages: make(map[protocol.DeviceID]protocol.Hello),
|
||||||
deviceDownloads: make(map[protocol.DeviceID]*deviceDownloadState),
|
deviceDownloads: make(map[protocol.DeviceID]*deviceDownloadState),
|
||||||
remotePausedFolders: make(map[protocol.DeviceID]map[string]struct{}),
|
remoteFolderStates: make(map[protocol.DeviceID]map[string]remoteFolderState),
|
||||||
indexHandlers: make(map[protocol.DeviceID]*indexHandlerRegistry),
|
indexHandlers: make(map[protocol.DeviceID]*indexHandlerRegistry),
|
||||||
}
|
}
|
||||||
for devID := range cfg.Devices() {
|
for devID := range cfg.Devices() {
|
||||||
@ -800,9 +800,10 @@ type FolderCompletion struct {
|
|||||||
NeedItems int
|
NeedItems int
|
||||||
NeedDeletes int
|
NeedDeletes int
|
||||||
Sequence int64
|
Sequence int64
|
||||||
|
Accepted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFolderCompletion(global, need db.Counts, sequence int64) FolderCompletion {
|
func newFolderCompletion(global, need db.Counts, sequence int64, accepted bool) FolderCompletion {
|
||||||
comp := FolderCompletion{
|
comp := FolderCompletion{
|
||||||
GlobalBytes: global.Bytes,
|
GlobalBytes: global.Bytes,
|
||||||
NeedBytes: need.Bytes,
|
NeedBytes: need.Bytes,
|
||||||
@ -810,6 +811,7 @@ func newFolderCompletion(global, need db.Counts, sequence int64) FolderCompletio
|
|||||||
NeedItems: need.Files + need.Directories + need.Symlinks,
|
NeedItems: need.Files + need.Directories + need.Symlinks,
|
||||||
NeedDeletes: need.Deleted,
|
NeedDeletes: need.Deleted,
|
||||||
Sequence: sequence,
|
Sequence: sequence,
|
||||||
|
Accepted: accepted,
|
||||||
}
|
}
|
||||||
comp.setComplectionPct()
|
comp.setComplectionPct()
|
||||||
return comp
|
return comp
|
||||||
@ -851,6 +853,7 @@ func (comp FolderCompletion) Map() map[string]interface{} {
|
|||||||
"needItems": comp.NeedItems,
|
"needItems": comp.NeedItems,
|
||||||
"needDeletes": comp.NeedDeletes,
|
"needDeletes": comp.NeedDeletes,
|
||||||
"sequence": comp.Sequence,
|
"sequence": comp.Sequence,
|
||||||
|
"accepted": comp.Accepted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,6 +904,7 @@ func (m *model) folderCompletion(device protocol.DeviceID, folder string) (Folde
|
|||||||
defer snap.Release()
|
defer snap.Release()
|
||||||
|
|
||||||
m.pmut.RLock()
|
m.pmut.RLock()
|
||||||
|
accepted := m.remoteFolderStates[device][folder] != remoteNotSharing
|
||||||
downloaded := m.deviceDownloads[device].BytesDownloaded(folder)
|
downloaded := m.deviceDownloads[device].BytesDownloaded(folder)
|
||||||
m.pmut.RUnlock()
|
m.pmut.RUnlock()
|
||||||
|
|
||||||
@ -911,7 +915,7 @@ func (m *model) folderCompletion(device protocol.DeviceID, folder string) (Folde
|
|||||||
need.Bytes = 0
|
need.Bytes = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
comp := newFolderCompletion(snap.GlobalSize(), need, snap.Sequence(device))
|
comp := newFolderCompletion(snap.GlobalSize(), need, snap.Sequence(device), accepted)
|
||||||
|
|
||||||
l.Debugf("%v Completion(%s, %q): %v", m, device, folder, comp.Map())
|
l.Debugf("%v Completion(%s, %q): %v", m, device, folder, comp.Map())
|
||||||
return comp, nil
|
return comp, nil
|
||||||
@ -1221,13 +1225,13 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
|
|||||||
w.Wait()
|
w.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
tempIndexFolders, paused, err := m.ccHandleFolders(cm.Folders, deviceCfg, ccDeviceInfos, indexHandlerRegistry)
|
tempIndexFolders, states, err := m.ccHandleFolders(cm.Folders, deviceCfg, ccDeviceInfos, indexHandlerRegistry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.pmut.Lock()
|
m.pmut.Lock()
|
||||||
m.remotePausedFolders[deviceID] = paused
|
m.remoteFolderStates[deviceID] = states
|
||||||
m.pmut.Unlock()
|
m.pmut.Unlock()
|
||||||
|
|
||||||
if len(tempIndexFolders) > 0 {
|
if len(tempIndexFolders) > 0 {
|
||||||
@ -1262,11 +1266,10 @@ func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.DeviceConfiguration, ccDeviceInfos map[string]*clusterConfigDeviceInfo, indexHandlers *indexHandlerRegistry) ([]string, map[string]struct{}, error) {
|
func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.DeviceConfiguration, ccDeviceInfos map[string]*clusterConfigDeviceInfo, indexHandlers *indexHandlerRegistry) ([]string, map[string]remoteFolderState, error) {
|
||||||
var folderDevice config.FolderDeviceConfiguration
|
var folderDevice config.FolderDeviceConfiguration
|
||||||
tempIndexFolders := make([]string, 0, len(folders))
|
tempIndexFolders := make([]string, 0, len(folders))
|
||||||
paused := make(map[string]struct{}, len(folders))
|
seenFolders := make(map[string]remoteFolderState, len(folders))
|
||||||
seenFolders := make(map[string]struct{}, len(folders))
|
|
||||||
updatedPending := make([]updatedPendingFolder, 0, len(folders))
|
updatedPending := make([]updatedPendingFolder, 0, len(folders))
|
||||||
deviceID := deviceCfg.DeviceID
|
deviceID := deviceCfg.DeviceID
|
||||||
expiredPending, err := m.db.PendingFoldersForDevice(deviceID)
|
expiredPending, err := m.db.PendingFoldersForDevice(deviceID)
|
||||||
@ -1275,7 +1278,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
|
|||||||
}
|
}
|
||||||
of := db.ObservedFolder{Time: time.Now().Truncate(time.Second)}
|
of := db.ObservedFolder{Time: time.Now().Truncate(time.Second)}
|
||||||
for _, folder := range folders {
|
for _, folder := range folders {
|
||||||
seenFolders[folder.ID] = struct{}{}
|
seenFolders[folder.ID] = remoteValid
|
||||||
|
|
||||||
cfg, ok := m.cfg.Folder(folder.ID)
|
cfg, ok := m.cfg.Folder(folder.ID)
|
||||||
if ok {
|
if ok {
|
||||||
@ -1316,7 +1319,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
|
|||||||
|
|
||||||
if folder.Paused {
|
if folder.Paused {
|
||||||
indexHandlers.Remove(folder.ID)
|
indexHandlers.Remove(folder.ID)
|
||||||
paused[cfg.ID] = struct{}{}
|
seenFolders[cfg.ID] = remotePaused
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1345,7 +1348,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
|
|||||||
m.evLogger.Log(events.Failure, err.Error())
|
m.evLogger.Log(events.Failure, err.Error())
|
||||||
l.Warnln(msg)
|
l.Warnln(msg)
|
||||||
}
|
}
|
||||||
return tempIndexFolders, paused, err
|
return tempIndexFolders, seenFolders, err
|
||||||
}
|
}
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
|
if devErrs, ok := m.folderEncryptionFailures[folder.ID]; ok {
|
||||||
@ -1367,6 +1370,15 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexHandlers.RemoveAllExcept(seenFolders)
|
indexHandlers.RemoveAllExcept(seenFolders)
|
||||||
|
|
||||||
|
// Explicitly mark folders we offer, but the remote has not accepted
|
||||||
|
for folderID, cfg := range m.cfg.Folders() {
|
||||||
|
if _, seen := seenFolders[folderID]; !seen && cfg.SharedWith(deviceID) {
|
||||||
|
l.Debugf("Remote device %v has not accepted folder %s", deviceID.Short(), cfg.Description())
|
||||||
|
seenFolders[folderID] = remoteNotSharing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expiredPendingList := make([]map[string]string, 0, len(expiredPending))
|
expiredPendingList := make([]map[string]string, 0, len(expiredPending))
|
||||||
for folder := range expiredPending {
|
for folder := range expiredPending {
|
||||||
if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil {
|
if err = m.db.RemovePendingFolderForDevice(folder, deviceID); err != nil {
|
||||||
@ -1387,7 +1399,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return tempIndexFolders, paused, nil
|
return tempIndexFolders, seenFolders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice config.FolderDeviceConfiguration, ccDeviceInfos *clusterConfigDeviceInfo, deviceUntrusted bool) error {
|
func (m *model) ccCheckEncryption(fcfg config.FolderConfiguration, folderDevice config.FolderDeviceConfiguration, ccDeviceInfos *clusterConfigDeviceInfo, deviceUntrusted bool) error {
|
||||||
@ -1726,7 +1738,7 @@ func (m *model) Closed(device protocol.DeviceID, err error) {
|
|||||||
delete(m.connRequestLimiters, device)
|
delete(m.connRequestLimiters, device)
|
||||||
delete(m.helloMessages, device)
|
delete(m.helloMessages, device)
|
||||||
delete(m.deviceDownloads, device)
|
delete(m.deviceDownloads, device)
|
||||||
delete(m.remotePausedFolders, device)
|
delete(m.remoteFolderStates, device)
|
||||||
closed := m.closed[device]
|
closed := m.closed[device]
|
||||||
delete(m.closed, device)
|
delete(m.closed, device)
|
||||||
delete(m.indexHandlers, device)
|
delete(m.indexHandlers, device)
|
||||||
@ -2697,10 +2709,10 @@ func (m *model) availabilityInSnapshot(cfg config.FolderConfiguration, snap *db.
|
|||||||
func (m *model) availabilityInSnapshotPRlocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
|
func (m *model) availabilityInSnapshotPRlocked(cfg config.FolderConfiguration, snap *db.Snapshot, file protocol.FileInfo, block protocol.BlockInfo) []Availability {
|
||||||
var availabilities []Availability
|
var availabilities []Availability
|
||||||
for _, device := range snap.Availability(file.Name) {
|
for _, device := range snap.Availability(file.Name) {
|
||||||
if _, ok := m.remotePausedFolders[device]; !ok {
|
if _, ok := m.remoteFolderStates[device]; !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := m.remotePausedFolders[device][cfg.ID]; ok {
|
if state, ok := m.remoteFolderStates[device][cfg.ID]; !ok || state == remotePaused {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, ok := m.conn[device]
|
_, ok := m.conn[device]
|
||||||
|
@ -3766,16 +3766,16 @@ func TestClusterConfigOnFolderUnpause(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddFolderCompletion(t *testing.T) {
|
func TestAddFolderCompletion(t *testing.T) {
|
||||||
// Empty folders are always 100% complete.
|
// Empty folders are always 100% complete.
|
||||||
comp := newFolderCompletion(db.Counts{}, db.Counts{}, 0)
|
comp := newFolderCompletion(db.Counts{}, db.Counts{}, 0, true)
|
||||||
comp.add(newFolderCompletion(db.Counts{}, db.Counts{}, 0))
|
comp.add(newFolderCompletion(db.Counts{}, db.Counts{}, 0, false))
|
||||||
if comp.CompletionPct != 100 {
|
if comp.CompletionPct != 100 {
|
||||||
t.Error(comp.CompletionPct)
|
t.Error(comp.CompletionPct)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Completion is of the whole
|
// Completion is of the whole
|
||||||
comp = newFolderCompletion(db.Counts{Bytes: 100}, db.Counts{}, 0) // 100% complete
|
comp = newFolderCompletion(db.Counts{Bytes: 100}, db.Counts{}, 0, true) // 100% complete
|
||||||
comp.add(newFolderCompletion(db.Counts{Bytes: 400}, db.Counts{Bytes: 50}, 0)) // 82.5% complete
|
comp.add(newFolderCompletion(db.Counts{Bytes: 400}, db.Counts{Bytes: 50}, 0, true)) // 82.5% complete
|
||||||
if comp.CompletionPct != 90 { // 100 * (1 - 50/500)
|
if comp.CompletionPct != 90 { // 100 * (1 - 50/500)
|
||||||
t.Error(comp.CompletionPct)
|
t.Error(comp.CompletionPct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ func (s *verboseService) formatEvent(ev events.Event) string {
|
|||||||
|
|
||||||
case events.FolderCompletion:
|
case events.FolderCompletion:
|
||||||
data := ev.Data.(map[string]interface{})
|
data := ev.Data.(map[string]interface{})
|
||||||
return fmt.Sprintf("Completion for folder %q on device %v is %v%%", data["folder"], data["device"], data["completion"])
|
return fmt.Sprintf("Completion for folder %q on device %v is %v%% (accepted: %v)", data["folder"], data["device"], data["completion"], data["accepted"])
|
||||||
|
|
||||||
case events.FolderSummary:
|
case events.FolderSummary:
|
||||||
data := ev.Data.(model.FolderSummaryEventData)
|
data := ev.Data.(model.FolderSummaryEventData)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user