diff --git a/CHANGELOG.md b/CHANGELOG.md index 24d07b33..b30ea567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ Changelog ========= +v1.1.22 +------- + + - Fix some crashes (#443, #463, #468, #471) + - Show the folder name, not ID, in the file transfers progress tooltip (#457) + - Add tray button to rescan all folders (#465) + - Show change in Syncthing theme without requiring refresh (#472) + - Allow STTRACE to be set in env vars in settings (#473) + - Fix slow memory leak (#479) + v1.1.21 ------- diff --git a/server/version_check.php b/server/version_check.php index cdefbaa3..8771add4 100644 --- a/server/version_check.php +++ b/server/version_check.php @@ -65,6 +65,25 @@ function get_with_wildcard($src, $value, $default = null) } $versions = [ + '1.1.22' => [ + 'base_url' => 'https://github.com/canton7/SyncTrayzor/releases/download', + 'installed' => [ + 'direct_download_url' => [ + 'x64' => "{base_url}/v{version}/SyncTrayzorSetup-x64.exe", + 'x86' => "{base_url}/v{version}/SyncTrayzorSetup-x86.exe", + ], + ], + 'portable' => [ + 'direct_download_url' => [ + 'x64' => "{base_url}/v{version}/SyncTrayzorPortable-x64.zip", + 'x86' => "{base_url}/v{version}/SyncTrayzorPortable-x86.zip", + ], + ], + 'sha1sum_download_url' => "{base_url}/v{version}/sha1sum.txt.asc", + 'sha512sum_download_url' => "{base_url}/v{version}/sha512sum.txt.asc", + 'release_page_url' => 'https://github.com/canton7/SyncTrayzor/releases/tag/v{version}', + 'release_notes' => "- Fix some crashes (#443, #463, #468, #471)\n- Show the folder name, not ID, in the file transfers progress tooltip (#457)\n- Add tray button to rescan all folders (#465)\n- Show change in Syncthing theme without requiring refresh (#472)\n- Allow STTRACE to be set in env vars in settings (#473)\n- Fix slow memory leak (#479)", + ], '1.1.21' => [ 'base_url' => 'https://synctrayzor.antonymale.co.uk/download', 'installed' => [ @@ -82,55 +101,56 @@ function get_with_wildcard($src, $value, $default = null) 'sha1sum_download_url' => "{base_url}/v{version}/sha1sum.txt.asc", 'sha512sum_download_url' => "{base_url}/v{version}/sha512sum.txt.asc", 'release_page_url' => 'https://github.com/canton7/SyncTrayzor/releases/tag/v{version}', - 'release_notes' => "!!!!!\nYou must upgrade to v1.1.21 now, otherwise auto-upgrades will stop working!\n!!!!!\n\n- Fix \"Syncthing failed to start correctly\" message when shutting down Windows (#438)\n- Handle \"Access denied\" errors when resolving conflicts (#440)", + 'release_notes' => "!!!!!\nYou must upgrade to v1.1.21 now, otherwise auto-upgrades will stop working!\n!!!!!\n\nFurther upgrades will be available once you have upgraded to v1.1.21.", ] ]; $upgrades = [ // Github start supporting tls3 only, and versions prior to 1.1.20 didn't support this. 1.1.20 and 1.1.21 are hosted on my server. 1.1.20 can download // directly from github, but versions prior have to use my server. - '1.1.20' => ['to' => 'latest', 'formatter' => '5', 'overrides' => ['base_url' => 'https://github.com/canton7/SyncTrayzor/releases/download', 'release_notes' => "- Fix \"Syncthing failed to start correctly\" message when shutting down Windows (#438)\n- Handle \"Access denied\" errors when resolving conflicts (#440)"]], - '1.1.19' => ['to' => 'latest', 'formatter' => '5'], - '1.1.18' => ['to' => 'latest', 'formatter' => '5'], - '1.1.17' => ['to' => 'latest', 'formatter' => '5'], - '1.1.16' => ['to' => 'latest', 'formatter' => '5'], - '1.1.15' => ['to' => 'latest', 'formatter' => '5'], - '1.1.14' => ['to' => 'latest', 'formatter' => '5'], - '1.1.13' => ['to' => 'latest', 'formatter' => '5'], - '1.1.12' => ['to' => 'latest', 'formatter' => '5'], - '1.1.11' => ['to' => 'latest', 'formatter' => '5'], - '1.1.10' => ['to' => 'latest', 'formatter' => '5'], - '1.1.9' => ['to' => 'latest', 'formatter' => '5'], - '1.1.8' => ['to' => 'latest', 'formatter' => '5'], - '1.1.7' => ['to' => 'latest', 'formatter' => '4'], - '1.1.6' => ['to' => 'latest', 'formatter' => '4'], - '1.1.5' => ['to' => 'latest', 'formatter' => '4'], - '1.1.4' => ['to' => 'latest', 'formatter' => '4'], - '1.1.3' => ['to' => 'latest', 'formatter' => '4'], - '1.1.2' => ['to' => 'latest', 'formatter' => '4'], - '1.1.1' => ['to' => 'latest', 'formatter' => '3'], - '1.1.0' => ['to' => 'latest', 'formatter' => '3'], - '1.0.32' => ['to' => 'latest', 'formatter' => '3'], - '1.0.31' => ['to' => 'latest', 'formatter' => '3'], - '1.0.30' => ['to' => 'latest', 'formatter' => '3'], - '1.0.29' => ['to' => 'latest', 'formatter' => '3'], - '1.0.28' => ['to' => 'latest', 'formatter' => '3'], - '1.0.27' => ['to' => 'latest', 'formatter' => '3'], - '1.0.26' => ['to' => 'latest', 'formatter' => '3'], - '1.0.25' => ['to' => 'latest', 'formatter' => '3'], - '1.0.24' => ['to' => 'latest', 'formatter' => '3'], - '1.0.23' => ['to' => 'latest', 'formatter' => '3'], - '1.0.22' => ['to' => 'latest', 'formatter' => '2'], - '1.0.21' => ['to' => 'latest', 'formatter' => '2'], - '1.0.20' => ['to' => 'latest', 'formatter' => '2'], + '1.1.21' => ['to' => 'latest', 'formatter' => '5'], + '1.1.20' => ['to' => 'latest', 'formatter' => '5'], + '1.1.19' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.18' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.17' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.16' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.15' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.14' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.13' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.12' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.11' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.10' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.9' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.8' => ['to' => '1.1.21', 'formatter' => '5'], + '1.1.7' => ['to' => '1.1.21', 'formatter' => '4'], + '1.1.6' => ['to' => '1.1.21', 'formatter' => '4'], + '1.1.5' => ['to' => '1.1.21', 'formatter' => '4'], + '1.1.4' => ['to' => '1.1.21', 'formatter' => '4'], + '1.1.3' => ['to' => '1.1.21', 'formatter' => '4'], + '1.1.2' => ['to' => '1.1.21', 'formatter' => '4'], + '1.1.1' => ['to' => '1.1.21', 'formatter' => '3'], + '1.1.0' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.32' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.31' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.30' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.29' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.28' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.27' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.26' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.25' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.24' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.23' => ['to' => '1.1.21', 'formatter' => '3'], + '1.0.22' => ['to' => '1.1.21', 'formatter' => '2'], + '1.0.21' => ['to' => '1.1.21', 'formatter' => '2'], + '1.0.20' => ['to' => '1.1.21', 'formatter' => '2'], // 1.0.19 was never actually released, so no need to represent it - '1.0.18' => ['to' => 'latest', 'formatter' => '2'], - '1.0.17' => ['to' => 'latest', 'formatter' => '2'], - '1.0.16' => ['to' => 'latest', 'formatter' => '2'], - '1.0.15' => ['to' => 'latest', 'formatter' => '2'], - '1.0.14' => ['to' => 'latest', 'formatter' => '2'], - '1.0.13' => ['to' => 'latest', 'formatter' => '1'], - '1.0.12' => ['to' => 'latest', 'formatter' => '1'], + '1.0.18' => ['to' => '1.1.21', 'formatter' => '2'], + '1.0.17' => ['to' => '1.1.21', 'formatter' => '2'], + '1.0.16' => ['to' => '1.1.21', 'formatter' => '2'], + '1.0.15' => ['to' => '1.1.21', 'formatter' => '2'], + '1.0.14' => ['to' => '1.1.21', 'formatter' => '2'], + '1.0.13' => ['to' => '1.1.21', 'formatter' => '1'], + '1.0.12' => ['to' => '1.1.21', 'formatter' => '1'], ]; $response_formatters = [ diff --git a/src/SyncTrayzor/Design/DummyFileTransfersTrayViewModel.cs b/src/SyncTrayzor/Design/DummyFileTransfersTrayViewModel.cs index 34c9911a..a19536c6 100644 --- a/src/SyncTrayzor/Design/DummyFileTransfersTrayViewModel.cs +++ b/src/SyncTrayzor/Design/DummyFileTransfersTrayViewModel.cs @@ -2,6 +2,7 @@ using SyncTrayzor.Pages; using SyncTrayzor.Pages.Tray; using SyncTrayzor.Syncthing.ApiClient; +using SyncTrayzor.Syncthing.Folders; using SyncTrayzor.Syncthing.TransferHistory; namespace SyncTrayzor.Design @@ -23,20 +24,21 @@ public DummyFileTransfersTrayViewModel() { this.CompletedTransfers = new BindableCollection(); this.InProgressTransfers = new BindableCollection(); + var folder = new Folder("folder", "Folder", "folderPath", false, FolderSyncState.Syncing, null); - var completedFileTransfer1 = new FileTransfer("folder", "path.pdf", ItemChangedItemType.File, ItemChangedActionType.Update); + var completedFileTransfer1 = new FileTransfer(folder, "path.pdf", ItemChangedItemType.File, ItemChangedActionType.Update); completedFileTransfer1.SetComplete(null, false); - var completedFileTransfer2 = new FileTransfer("folder", "a really very long path that's far too long to sit on the page.h", ItemChangedItemType.File, ItemChangedActionType.Delete); + var completedFileTransfer2 = new FileTransfer(folder, "a really very long path that's far too long to sit on the page.h", ItemChangedItemType.File, ItemChangedActionType.Delete); completedFileTransfer2.SetComplete("Something went very wrong", true); //this.CompletedTransfers.Add(new FileTransferViewModel(completedFileTransfer1)); this.CompletedTransfers.Add(new FileTransferViewModel(completedFileTransfer2)); - var inProgressTransfer1 = new FileTransfer("folder", "path.txt", ItemChangedItemType.File, ItemChangedActionType.Update); + var inProgressTransfer1 = new FileTransfer(folder, "path.txt", ItemChangedItemType.File, ItemChangedActionType.Update); inProgressTransfer1.SetDownloadProgress(5*1024*1024, 100*1024*1024); - var inProgressTransfer2 = new FileTransfer("folder", "path", ItemChangedItemType.Dir, ItemChangedActionType.Update); + var inProgressTransfer2 = new FileTransfer(folder, "path", ItemChangedItemType.Dir, ItemChangedActionType.Update); this.InProgressTransfers.Add(new FileTransferViewModel(inProgressTransfer1)); this.InProgressTransfers.Add(new FileTransferViewModel(inProgressTransfer2)); diff --git a/src/SyncTrayzor/NotifyIcon/NotifyIconResolutionUtilities.cs b/src/SyncTrayzor/NotifyIcon/NotifyIconResolutionUtilities.cs index 2d8c1ddc..cb60afb9 100644 --- a/src/SyncTrayzor/NotifyIcon/NotifyIconResolutionUtilities.cs +++ b/src/SyncTrayzor/NotifyIcon/NotifyIconResolutionUtilities.cs @@ -41,16 +41,21 @@ private static void SetIconFromImageSource(TaskbarIcon taskbarIcon, ImageSource if (imageSource != null) { Uri uri = new Uri(imageSource.ToString()); - StreamResourceInfo streamInfo = Application.GetResourceStream(uri); + // The Icon ctor doesn't take ownership of the stream. This stream *does* however + // need to be disposed: PackagePart keeps a cache of streams which have been requested, + // and only releases them once they've been disposed, so the finalizer never gets a chance + // to kick in. See #479. + using (var stream = Application.GetResourceStream(uri)?.Stream) + { + if (stream == null) + { + string msg = "The supplied image source '{0}' could not be resolved."; + msg = String.Format(msg, imageSource); + throw new ArgumentException(msg); + } - if (streamInfo == null) - { - string msg = "The supplied image source '{0}' could not be resolved."; - msg = String.Format(msg, imageSource); - throw new ArgumentException(msg); + icon = new Icon(stream, idealIconSize, idealIconSize); } - - icon = new Icon(streamInfo.Stream, idealIconSize, idealIconSize); } taskbarIcon.Icon?.Dispose(); diff --git a/src/SyncTrayzor/NotifyIcon/NotifyIconViewModel.cs b/src/SyncTrayzor/NotifyIcon/NotifyIconViewModel.cs index 34eaee33..49929c49 100644 --- a/src/SyncTrayzor/NotifyIcon/NotifyIconViewModel.cs +++ b/src/SyncTrayzor/NotifyIcon/NotifyIconViewModel.cs @@ -158,6 +158,12 @@ public void Restart() this.syncthingManager.RestartAsync(); } + public bool CanRescanAll => this.SyncthingState == SyncthingState.Running; + public void RescanAll() + { + this.syncthingManager.ScanAsync(null, null); + } + public void Exit() { this.OnExitRequested(); diff --git a/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml b/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml index 25efc4fa..89ce6b70 100644 --- a/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml +++ b/src/SyncTrayzor/NotifyIcon/TaskbarIconResources.xaml @@ -109,7 +109,8 @@ - + + diff --git a/src/SyncTrayzor/Pages/Settings/SettingsViewModel.cs b/src/SyncTrayzor/Pages/Settings/SettingsViewModel.cs index 4608b9ca..be953cda 100644 --- a/src/SyncTrayzor/Pages/Settings/SettingsViewModel.cs +++ b/src/SyncTrayzor/Pages/Settings/SettingsViewModel.cs @@ -289,19 +289,23 @@ private void LoadFromSyncthingStartupData() this.FolderSettings.Clear(); - var folderSettings = configuration.Folders.Select(x => - { - this.syncthingManager.Folders.TryFetchById(x.ID, out var folder); + var folderSettings = new List(configuration.Folders.Count); - return new FolderSettings() + foreach (var configFolder in configuration.Folders) + { + if (this.syncthingManager.Folders.TryFetchById(configFolder.ID, out var folder)) { - FolderId = x.ID, - FolderLabel = folder?.Label ?? x.ID, - IsWatched = x.IsWatched, - IsWatchAllowed = !folder.IsFsWatcherEnabled, - IsNotified = x.NotificationsEnabled, - }; - }); + folderSettings.Add(new FolderSettings() + { + FolderId = configFolder.ID, + FolderLabel = folder?.Label ?? configFolder.ID, + IsWatched = configFolder.IsWatched, + IsWatchAllowed = !folder.IsFsWatcherEnabled, + IsNotified = configFolder.NotificationsEnabled, + }); + } + } + this.FolderSettings.AddRange(folderSettings.OrderBy(x => x.FolderLabel)); this.IsAnyFolderWatchEnabledInSyncthing = this.FolderSettings.Any(x => !x.IsWatchAllowed); diff --git a/src/SyncTrayzor/Pages/Settings/SyncthingEnvironmentalVariablesValidator.cs b/src/SyncTrayzor/Pages/Settings/SyncthingEnvironmentalVariablesValidator.cs index 3bf3c9df..fe8d1160 100644 --- a/src/SyncTrayzor/Pages/Settings/SyncthingEnvironmentalVariablesValidator.cs +++ b/src/SyncTrayzor/Pages/Settings/SyncthingEnvironmentalVariablesValidator.cs @@ -12,12 +12,6 @@ public SyncthingEnvironmentalVariablesValidator() { return KeyValueStringParser.TryParse(str, out var result); }).WithMessage(Resources.SettingsView_Validation_SyncthingEnvironmentalVariablesMustHaveFormat); - - RuleFor(x => x.Value).Must(str => - { - KeyValueStringParser.TryParse(str, out var result); - return !result.Any(x => x.Key == "STTRACE"); - }).WithMessage(Resources.SettingsView_Validation_SetSttraceInTab); } } } diff --git a/src/SyncTrayzor/Pages/Tray/FileTransferViewModel.cs b/src/SyncTrayzor/Pages/Tray/FileTransferViewModel.cs index b37f90ea..93cfa8d4 100644 --- a/src/SyncTrayzor/Pages/Tray/FileTransferViewModel.cs +++ b/src/SyncTrayzor/Pages/Tray/FileTransferViewModel.cs @@ -1,6 +1,7 @@ using Stylet; using SyncTrayzor.Properties; using SyncTrayzor.Syncthing.ApiClient; +using SyncTrayzor.Syncthing.Folders; using SyncTrayzor.Syncthing.TransferHistory; using SyncTrayzor.Utils; using System; @@ -22,7 +23,7 @@ public class FileTransferViewModel : PropertyChangedBase private readonly DispatcherTimer completedTimeAgoUpdateTimer; public string Path { get; } - public string FolderId { get; } + public Folder Folder { get; } public string FullPath { get; } public ImageSource Icon { get; } public string Error { get; private set; } @@ -56,7 +57,7 @@ public FileTransferViewModel(FileTransfer fileTransfer) this.FileTransfer = fileTransfer; this.Path = Pri.LongPath.Path.GetFileName(this.FileTransfer.Path); this.FullPath = this.FileTransfer.Path; - this.FolderId = this.FileTransfer.FolderId; + this.Folder = this.FileTransfer.Folder; using (var icon = ShellTools.GetIcon(this.FileTransfer.Path, this.FileTransfer.ItemType != ItemChangedItemType.Dir)) { var bs = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); diff --git a/src/SyncTrayzor/Pages/Tray/FileTransfersTrayView.xaml b/src/SyncTrayzor/Pages/Tray/FileTransfersTrayView.xaml index 3257635d..3a74d015 100644 --- a/src/SyncTrayzor/Pages/Tray/FileTransfersTrayView.xaml +++ b/src/SyncTrayzor/Pages/Tray/FileTransfersTrayView.xaml @@ -126,7 +126,7 @@ - + diff --git a/src/SyncTrayzor/Pages/Tray/FileTransfersTrayViewModel.cs b/src/SyncTrayzor/Pages/Tray/FileTransfersTrayViewModel.cs index 876c1404..89776f17 100644 --- a/src/SyncTrayzor/Pages/Tray/FileTransfersTrayViewModel.cs +++ b/src/SyncTrayzor/Pages/Tray/FileTransfersTrayViewModel.cs @@ -137,16 +137,14 @@ private void UpdateConnectionStats(double? inBytesPerSecond, double? outBytesPer public void ItemClicked(FileTransferViewModel fileTransferVm) { var fileTransfer = fileTransferVm.FileTransfer; - if (!this.syncthingManager.Folders.TryFetchById(fileTransfer.FolderId, out var folder)) - return; // Huh? Nothing we can do about it... // Not sure of the best way to deal with deletions yet... if (fileTransfer.ActionType == ItemChangedActionType.Update) { if (fileTransfer.ItemType == ItemChangedItemType.File) - this.processStartProvider.ShowFileInExplorer(Path.Combine(folder.Path, fileTransfer.Path)); + this.processStartProvider.ShowFileInExplorer(Path.Combine(fileTransferVm.Folder.Path, fileTransfer.Path)); else if (fileTransfer.ItemType == ItemChangedItemType.Dir) - this.processStartProvider.ShowFolderInExplorer(Path.Combine(folder.Path, fileTransfer.Path)); + this.processStartProvider.ShowFolderInExplorer(Path.Combine(fileTransferVm.Folder.Path, fileTransfer.Path)); } } diff --git a/src/SyncTrayzor/Pages/ViewerViewModel.cs b/src/SyncTrayzor/Pages/ViewerViewModel.cs index 617e6de2..969fb782 100644 --- a/src/SyncTrayzor/Pages/ViewerViewModel.cs +++ b/src/SyncTrayzor/Pages/ViewerViewModel.cs @@ -99,16 +99,17 @@ protected override void OnInitialActivate() // We really only want to set the LocalStorage path, but we don't have that level of control.... CachePath = this.pathsProvider.CefCachePath, IgnoreCertificateErrors = true, + LogSeverity = LogSeverity.Disable, }; // System proxy settings (which also specify a proxy for localhost) shouldn't affect us settings.CefCommandLineArgs.Add("no-proxy-server", "1"); + settings.CefCommandLineArgs.Add("disable-cache", "1"); if (configuration.DisableHardwareRendering) { settings.CefCommandLineArgs.Add("disable-gpu", "1"); settings.CefCommandLineArgs.Add("disable-gpu-vsync", "1"); - settings.CefCommandLineArgs.Add("disable-cache", "1"); settings.CefCommandLineArgs.Add("disable-application-cache", "1"); } @@ -342,6 +343,12 @@ CefReturnValue IRequestHandler.OnBeforeResourceLoad(IWebBrowser browserControl, // and https://github.com/cefsharp/CefSharp/issues/534#issuecomment-60694502 var headers = request.Headers; headers["X-API-Key"] = this.syncthingManager.ApiKey; + + // I don't know why it adds these, even when we explicitly disable caching. + headers.Remove("Cache-Control"); + headers.Remove("If-None-Match"); + headers.Remove("If-Modified-Since"); + lock (this.cultureLock) { if (this.culture != null) diff --git a/src/SyncTrayzor/Properties/Resources.Designer.cs b/src/SyncTrayzor/Properties/Resources.Designer.cs index 225a030d..bee76379 100644 --- a/src/SyncTrayzor/Properties/Resources.Designer.cs +++ b/src/SyncTrayzor/Properties/Resources.Designer.cs @@ -1435,15 +1435,6 @@ public class Resources { } } - /// - /// Looks up a localized string similar to Set STTRACE in the 'Logging + STTRACE' tab. - /// - public static string SettingsView_Validation_SetSttraceInTab { - get { - return ResourceManager.GetString("SettingsView_Validation_SetSttraceInTab", resourceCulture); - } - } - /// /// Looks up a localized string similar to You may not specify the flag {0}. /// @@ -2050,6 +2041,15 @@ public class Resources { } } + /// + /// Looks up a localized string similar to _Rescan All Folders. + /// + public static string TrayIcon_Menu_RescanAllFolders { + get { + return ResourceManager.GetString("TrayIcon_Menu_RescanAllFolders", resourceCulture); + } + } + /// /// Looks up a localized string similar to _Restart Syncthing. /// diff --git a/src/SyncTrayzor/Properties/Resources.resx b/src/SyncTrayzor/Properties/Resources.resx index 015d6276..31376dda 100644 --- a/src/SyncTrayzor/Properties/Resources.resx +++ b/src/SyncTrayzor/Properties/Resources.resx @@ -727,10 +727,6 @@ Do you want to restart SyncTrayzor now? Logging - - Set STTRACE in the 'Logging + STTRACE' tab - Shown when the user attempts to enter an STTRACE value in the 'Syncthing Environmental Variables' box. They should use the tab with the heading 'Logging +STTRACE' instead. - Settings @@ -991,4 +987,8 @@ Please donate to my charity fundraising campaign. Syncthing Custom Path + + _Rescan All Folders + Menu option available when right-clicking the tray icon. Allows the user to re-scan all folders + \ No newline at end of file diff --git a/src/SyncTrayzor/Services/ApplicationWindowState.cs b/src/SyncTrayzor/Services/ApplicationWindowState.cs index 0a84106d..996a157a 100644 --- a/src/SyncTrayzor/Services/ApplicationWindowState.cs +++ b/src/SyncTrayzor/Services/ApplicationWindowState.cs @@ -64,9 +64,12 @@ public void EnsureInForeground() public void Dispose() { - this.rootViewModel.Activated -= this.OnRootWindowActivated; - this.rootViewModel.Deactivated -= this.OnRootWindowDeactivated; - this.rootViewModel.Closed -= this.OnRootWindowClosed; + if (this.rootViewModel != null) + { + this.rootViewModel.Activated -= this.OnRootWindowActivated; + this.rootViewModel.Deactivated -= this.OnRootWindowDeactivated; + this.rootViewModel.Closed -= this.OnRootWindowClosed; + } } } } diff --git a/src/SyncTrayzor/Services/ConfigurationApplicator.cs b/src/SyncTrayzor/Services/ConfigurationApplicator.cs index ded00dec..a1cd760d 100644 --- a/src/SyncTrayzor/Services/ConfigurationApplicator.cs +++ b/src/SyncTrayzor/Services/ConfigurationApplicator.cs @@ -52,7 +52,7 @@ public class ConfigurationApplicator : IDisposable this.meteredNetworkManager = meteredNetworkManager; this.pathTransformer = pathTransformer; - this.syncthingManager.DataLoaded += this.OnDataLoaded; + this.syncthingManager.Folders.FoldersChanged += this.FoldersChanged; this.updateManager.VersionIgnored += this.VersionIgnored; } @@ -144,7 +144,7 @@ private static void SetLogLevel(Configuration configuration) } } - private void OnDataLoaded(object sender, EventArgs e) + private void FoldersChanged(object sender, EventArgs e) { this.configurationProvider.AtomicLoadAndSave(c => { @@ -171,7 +171,7 @@ private void LoadFolders(Configuration configuration) public void Dispose() { this.configurationProvider.ConfigurationChanged -= this.ConfigurationChanged; - this.syncthingManager.DataLoaded -= this.OnDataLoaded; + this.syncthingManager.Folders.FoldersChanged -= this.FoldersChanged; this.updateManager.VersionIgnored -= this.VersionIgnored; } } diff --git a/src/SyncTrayzor/Services/Ipc/IpcCommsServer.cs b/src/SyncTrayzor/Services/Ipc/IpcCommsServer.cs index e3ffc57f..a252673e 100644 --- a/src/SyncTrayzor/Services/Ipc/IpcCommsServer.cs +++ b/src/SyncTrayzor/Services/Ipc/IpcCommsServer.cs @@ -52,33 +52,49 @@ private async void StartInternal(CancellationToken cancellationToken) byte[] buffer = new byte[256]; var commandBuilder = new StringBuilder(); - var serverStream = new NamedPipeServerStream(this.PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 0, 0); - - using (cancellationToken.Register(() => serverStream.Close())) + try { - while (!cancellationToken.IsCancellationRequested) - { - await Task.Factory.FromAsync(serverStream.BeginWaitForConnection, serverStream.EndWaitForConnection, TaskCreationOptions.None); - - int read = await serverStream.ReadAsync(buffer, 0, buffer.Length); - commandBuilder.Append(Encoding.ASCII.GetString(buffer, 0, read)); + var serverStream = new NamedPipeServerStream(this.PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 0, 0); - while (!serverStream.IsMessageComplete) + using (cancellationToken.Register(() => serverStream.Close())) + { + while (!cancellationToken.IsCancellationRequested) { - read = serverStream.Read(buffer, 0, buffer.Length); + await Task.Factory.FromAsync(serverStream.BeginWaitForConnection, serverStream.EndWaitForConnection, TaskCreationOptions.None); + + int read = await serverStream.ReadAsync(buffer, 0, buffer.Length); commandBuilder.Append(Encoding.ASCII.GetString(buffer, 0, read)); - } - var response = this.HandleReceivedCommand(commandBuilder.ToString()); - var responseBytes = Encoding.ASCII.GetBytes(response); - serverStream.Write(responseBytes, 0, responseBytes.Length); + while (!serverStream.IsMessageComplete) + { + read = serverStream.Read(buffer, 0, buffer.Length); + commandBuilder.Append(Encoding.ASCII.GetString(buffer, 0, read)); + } - serverStream.WaitForPipeDrain(); - serverStream.Disconnect(); + var response = this.HandleReceivedCommand(commandBuilder.ToString()); + var responseBytes = Encoding.ASCII.GetBytes(response); - commandBuilder.Clear(); + try + { + serverStream.Write(responseBytes, 0, responseBytes.Length); + + serverStream.WaitForPipeDrain(); + } + catch (Exception e) + { + logger.Error(e, "Unable to write response to pipe"); + } + + serverStream.Disconnect(); + + commandBuilder.Clear(); + } } } + catch (Exception e) + { + logger.Error(e, "Pipe server threw an exception"); + } } private string HandleReceivedCommand(string command) diff --git a/src/SyncTrayzor/Syncthing/SyncthingProcessRunner.cs b/src/SyncTrayzor/Syncthing/SyncthingProcessRunner.cs index fe2577f4..21c0390a 100644 --- a/src/SyncTrayzor/Syncthing/SyncthingProcessRunner.cs +++ b/src/SyncTrayzor/Syncthing/SyncthingProcessRunner.cs @@ -251,6 +251,7 @@ private void OnProcessExited() { logger.Warn("{0} restarts in less than {1}: not restarting again", numRestarts, restartThreshold); this.OnProcessStopped(exitStatus); + this.starts.Clear(); } else { diff --git a/src/SyncTrayzor/Syncthing/TransferHistory/FileTransfer.cs b/src/SyncTrayzor/Syncthing/TransferHistory/FileTransfer.cs index 93e736d9..9792de4f 100644 --- a/src/SyncTrayzor/Syncthing/TransferHistory/FileTransfer.cs +++ b/src/SyncTrayzor/Syncthing/TransferHistory/FileTransfer.cs @@ -1,4 +1,5 @@ using SyncTrayzor.Syncthing.ApiClient; +using SyncTrayzor.Syncthing.Folders; using System; namespace SyncTrayzor.Syncthing.TransferHistory @@ -11,7 +12,7 @@ public class FileTransfer public long TotalBytes { get; private set; } public double? DownloadBytesPerSecond { get; private set; } - public string FolderId { get; } + public Folder Folder { get; } public string Path { get; } public ItemChangedItemType ItemType { get; } public ItemChangedActionType ActionType { get; } @@ -24,9 +25,9 @@ public class FileTransfer private DateTime? lastProgressUpdateUtc; - public FileTransfer(string folderId, string path, ItemChangedItemType itemType, ItemChangedActionType actionType) + public FileTransfer(Folder folder, string path, ItemChangedItemType itemType, ItemChangedActionType actionType) { - this.FolderId = folderId; + this.Folder = folder; this.Path = path; this.Status = FileTransferStatus.Started; @@ -61,7 +62,7 @@ public void SetComplete(string error, bool isNewError) public override string ToString() { - return $""; + return $""; } } } diff --git a/src/SyncTrayzor/Syncthing/TransferHistory/SyncthingTransferHistory.cs b/src/SyncTrayzor/Syncthing/TransferHistory/SyncthingTransferHistory.cs index 9138bd03..b606deef 100644 --- a/src/SyncTrayzor/Syncthing/TransferHistory/SyncthingTransferHistory.cs +++ b/src/SyncTrayzor/Syncthing/TransferHistory/SyncthingTransferHistory.cs @@ -98,14 +98,15 @@ public SyncthingTransferHistory(ISyncthingEventWatcher eventWatcher, ISyncthingF this.folderManager.SyncStateChanged += this.SyncStateChanged; } - private FileTransfer FetchOrInsertInProgressFileTransfer(string folder, string path, ItemChangedItemType itemType, ItemChangedActionType actionType) + private FileTransfer FetchOrInsertInProgressFileTransfer(string folderId, string path, ItemChangedItemType itemType, ItemChangedActionType actionType) { - var key = new FolderPathKey(folder, path); + var key = new FolderPathKey(folderId, path); bool created = false; FileTransfer fileTransfer; lock (this.transfersLock) { - if (!this.inProgressTransfers.TryGetValue(key, out fileTransfer)) + if (!this.inProgressTransfers.TryGetValue(key, out fileTransfer) && + this.folderManager.TryFetchById(folderId, out var folder)) { created = true; fileTransfer = new FileTransfer(folder, path, itemType, actionType); @@ -116,6 +117,7 @@ private FileTransfer FetchOrInsertInProgressFileTransfer(string folder, string p if (created) this.OnTransferStarted(fileTransfer); + return fileTransfer; } @@ -149,12 +151,18 @@ private void ItemFinished(object sender, ItemFinishedEventArgs e) lock (this.transfersLock) { fileTransfer = this.FetchOrInsertInProgressFileTransfer(e.Folder, e.Item, e.ItemType, e.Action); - - this.CompleteFileTransfer(fileTransfer, e.Error); + // If it wasn't, and we couldn't create it, fileTransfer is null + if (fileTransfer != null) + { + this.CompleteFileTransfer(fileTransfer, e.Error); + } } - this.OnTransferStateChanged(fileTransfer); - this.OnTransferCompleted(fileTransfer); + if (fileTransfer != null) + { + this.OnTransferStateChanged(fileTransfer); + this.OnTransferCompleted(fileTransfer); + } } private void CompleteFileTransfer(FileTransfer fileTransfer, string error) @@ -162,7 +170,7 @@ private void CompleteFileTransfer(FileTransfer fileTransfer, string error) // This is always called from within a lock, but you can't be too sure... lock (this.transfersLock) { - var key = new FolderPathKey(fileTransfer.FolderId, fileTransfer.Path); + var key = new FolderPathKey(fileTransfer.Folder.FolderId, fileTransfer.Path); bool isNewError = false; if (error == null) @@ -175,7 +183,7 @@ private void CompleteFileTransfer(FileTransfer fileTransfer, string error) { // Remove will only do something in the case that the failure existed, but the error changed this.currentlyFailingTransfers.Remove(key); - this.currentlyFailingTransfers.Add(key, new FailingTransfer(fileTransfer.FolderId, fileTransfer.Path, error)); + this.currentlyFailingTransfers.Add(key, new FailingTransfer(fileTransfer.Folder.FolderId, fileTransfer.Path, error)); isNewError = true; } } @@ -189,10 +197,10 @@ private void CompleteFileTransfer(FileTransfer fileTransfer, string error) if (this.completedTransfers.Count > maxCompletedTransfers) this.completedTransfers.Dequeue(); - if (!this.recentlySynchronized.TryGetValue(fileTransfer.FolderId, out var recentlySynchronizedList)) + if (!this.recentlySynchronized.TryGetValue(fileTransfer.Folder.FolderId, out var recentlySynchronizedList)) { recentlySynchronizedList = new List(); - this.recentlySynchronized[fileTransfer.FolderId] = recentlySynchronizedList; + this.recentlySynchronized[fileTransfer.Folder.FolderId] = recentlySynchronizedList; } recentlySynchronizedList.Add(fileTransfer); } diff --git a/src/SyncTrayzor/Xaml/GridViewSortByBehaviour.cs b/src/SyncTrayzor/Xaml/GridViewSortByBehaviour.cs index 78768787..65dedff6 100644 --- a/src/SyncTrayzor/Xaml/GridViewSortByBehaviour.cs +++ b/src/SyncTrayzor/Xaml/GridViewSortByBehaviour.cs @@ -123,6 +123,10 @@ private void PerformInitialSort() private void SortBy(GridViewColumnHeader header) { + // Not entirely sure how this can happen, but it's been reported in the wild + if (header.Column == null) + return; + var direction = ListSortDirection.Ascending; if (header == this.lastColumnHeader) direction = (this.lastDirection == ListSortDirection.Ascending) ? ListSortDirection.Descending : ListSortDirection.Ascending; @@ -131,7 +135,7 @@ private void SortBy(GridViewColumnHeader header) } private void ApplyColumnSort(GridViewColumnHeader header, GridViewColumn column, ListSortDirection direction) - { + { var propertyName = GridViewSortBy.GetSortByKey(column); if (propertyName == null) return;