diff --git a/gui/default/index.html b/gui/default/index.html index ec673d89..655a46fc 100644 --- a/gui/default/index.html +++ b/gui/default/index.html @@ -325,7 +325,7 @@ - ({{syncPercentage(folder.id) | percent}}, {{syncRemaining(folder.id) | binary}}B) + ({{syncPercentage(folder.id) | percent}}, {{model[folder.id].needBytes | binary}}B) diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js index ea7a97f1..c19b9f43 100755 --- a/gui/default/syncthing/core/syncthingController.js +++ b/gui/default/syncthing/core/syncthingController.js @@ -816,22 +816,6 @@ angular.module('syncthing.core') return Math.floor(pct); }; - $scope.syncRemaining = function (folder) { - // Remaining sync bytes - if (typeof $scope.model[folder] === 'undefined') { - return 0; - } - if ($scope.model[folder].globalBytes === 0) { - return 0; - } - - var bytes = $scope.model[folder].globalBytes - $scope.model[folder].inSyncBytes; - if (isNaN(bytes) || bytes < 0) { - return 0; - } - return bytes; - }; - $scope.scanPercentage = function (folder) { if (!$scope.scanProgress[folder]) { return undefined; diff --git a/lib/model/folder_summary.go b/lib/model/folder_summary.go index 195afb0d..4848de99 100644 --- a/lib/model/folder_summary.go +++ b/lib/model/folder_summary.go @@ -149,7 +149,7 @@ func (c *folderSummaryService) OnEventRequest() { // listenForUpdates subscribes to the event bus and makes note of folders that // need their data recalculated. func (c *folderSummaryService) listenForUpdates() { - sub := events.Default.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated | events.StateChanged | events.RemoteDownloadProgress | events.DeviceConnected | events.FolderWatchStateChanged) + sub := events.Default.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated | events.StateChanged | events.RemoteDownloadProgress | events.DeviceConnected | events.FolderWatchStateChanged | events.DownloadProgress) defer events.Default.Unsubscribe(sub) for { @@ -157,68 +157,84 @@ func (c *folderSummaryService) listenForUpdates() { select { case ev := <-sub.C(): - if ev.Type == events.DeviceConnected { - // When a device connects we schedule a refresh of all - // folders shared with that device. - - data := ev.Data.(map[string]string) - deviceID, _ := protocol.DeviceIDFromString(data["id"]) - - c.foldersMut.Lock() - nextFolder: - for _, folder := range c.cfg.Folders() { - for _, dev := range folder.Devices { - if dev.DeviceID == deviceID { - c.folders[folder.ID] = struct{}{} - continue nextFolder - } - } - } - c.foldersMut.Unlock() - - continue - } - - // The other events all have a "folder" attribute that they - // affect. Whenever the local or remote index is updated for a - // given folder we make a note of it. - - data := ev.Data.(map[string]interface{}) - folder := data["folder"].(string) - - switch ev.Type { - case events.StateChanged: - if data["to"].(string) == "idle" && data["from"].(string) == "syncing" { - // The folder changed to idle from syncing. We should do an - // immediate refresh to update the GUI. The send to - // c.immediate must be nonblocking so that we can continue - // handling events. - - c.foldersMut.Lock() - select { - case c.immediate <- folder: - delete(c.folders, folder) - default: - c.folders[folder] = struct{}{} - } - c.foldersMut.Unlock() - } - - default: - // This folder needs to be refreshed whenever we do the next - // refresh. - - c.foldersMut.Lock() - c.folders[folder] = struct{}{} - c.foldersMut.Unlock() - } - + c.processUpdate(ev) case <-c.stop: return } } } +func (c *folderSummaryService) processUpdate(ev events.Event) { + var folder string + + switch ev.Type { + case events.DeviceConnected: + // When a device connects we schedule a refresh of all + // folders shared with that device. + + data := ev.Data.(map[string]string) + deviceID, _ := protocol.DeviceIDFromString(data["id"]) + + c.foldersMut.Lock() + nextFolder: + for _, folder := range c.cfg.Folders() { + for _, dev := range folder.Devices { + if dev.DeviceID == deviceID { + c.folders[folder.ID] = struct{}{} + continue nextFolder + } + } + } + c.foldersMut.Unlock() + + return + + case events.DownloadProgress: + data := ev.Data.(map[string]map[string]*pullerProgress) + c.foldersMut.Lock() + for folder := range data { + c.folders[folder] = struct{}{} + } + c.foldersMut.Unlock() + return + + case events.StateChanged: + data := ev.Data.(map[string]interface{}) + if !(data["to"].(string) == "idle" && data["from"].(string) == "syncing") { + return + } + + // The folder changed to idle from syncing. We should do an + // immediate refresh to update the GUI. The send to + // c.immediate must be nonblocking so that we can continue + // handling events. + + folder = data["folder"].(string) + select { + case c.immediate <- folder: + c.foldersMut.Lock() + delete(c.folders, folder) + c.foldersMut.Unlock() + return + default: + // Refresh whenever we do the next summary. + } + + default: + // The other events all have a "folder" attribute that they + // affect. Whenever the local or remote index is updated for a + // given folder we make a note of it. + // This folder needs to be refreshed whenever we do the next + // refresh. + + folder = ev.Data.(map[string]interface{})["folder"].(string) + } + + c.foldersMut.Lock() + c.folders[folder] = struct{}{} + c.foldersMut.Unlock() +} + // calculateSummaries periodically recalculates folder summaries and // completion percentage, and sends the results on the event bus. func (c *folderSummaryService) calculateSummaries() { diff --git a/lib/model/model.go b/lib/model/model.go index 7e2e58d5..73917ddf 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -801,7 +801,8 @@ func (m *model) ReceiveOnlyChangedSize(folder string) db.Counts { return db.Counts{} } -// NeedSize returns the number and total size of currently needed files. +// NeedSize returns the number of currently needed files and their total size +// minus the amount that has already been downloaded. func (m *model) NeedSize(folder string) db.Counts { m.fmut.RLock() rf, ok := m.folderFiles[folder]