From c6d75cfff65361befc36c9d7aa669deab3dae470 Mon Sep 17 00:00:00 2001 From: Ben Wallis Date: Sat, 8 Sep 2018 22:25:48 +0100 Subject: [PATCH] Implemented auto-update using Squirrel Reworked SettingsPageView to not need a Save Button and added a folder browser control --- .../Filtration.Interface.csproj | 18 ++ Filtration.Interface/IDocument.cs | 3 + Filtration.Interface/packages.config | 5 + Filtration.sln | 135 +++++++++ Filtration.sln.DotSettings | 3 +- Filtration/App.config | 27 ++ Filtration/App.xaml.cs | 36 +-- Filtration/Filtration.csproj | 128 +++++++-- Filtration/Filtration.nuspec | 18 ++ Filtration/NLog.config | 2 + Filtration/Properties/AssemblyInfo.cs | 3 +- Filtration/Properties/Settings.Designer.cs | 110 ++++---- Filtration/Properties/Settings.settings | 27 ++ Filtration/Services/Bootstrapper.cs | 59 ++++ Filtration/Services/SettingsService.cs | 64 +++++ Filtration/Services/UpdateCheckService.cs | 137 ---------- Filtration/Services/UpdateService.cs | 258 ++++++++++++++++++ .../DesignTimeMainWindowViewModel.cs | 36 +++ .../DesignTimeSettingsPageViewModel.cs | 23 ++ .../DesignTime/DesignTimeUpdateViewModel.cs | 49 ++++ Filtration/ViewModels/MainWindowViewModel.cs | 12 +- .../ViewModels/SettingsPageViewModel.cs | 58 ++-- Filtration/ViewModels/StartPageViewModel.cs | 7 +- .../ViewModels/UpdateAvailableViewModel.cs | 79 ------ Filtration/ViewModels/UpdateViewModel.cs | 203 ++++++++++++++ Filtration/Views/AboutWindow.xaml.cs | 2 +- .../AvalonDock/AvalonDockWorkspaceView.xaml | 5 + .../Views/AvalonDock/PanesTemplateSelector.cs | 8 +- Filtration/Views/MainWindow.xaml | 6 +- Filtration/Views/SettingsPageView.xaml | 37 ++- Filtration/Views/UpdateAvailableView.xaml | 38 --- Filtration/Views/UpdateTabView.xaml | 39 +++ Filtration/Views/UpdateTabView.xaml.cs | 21 ++ Filtration/Views/UpdateView.xaml | 41 +++ ...ailableView.xaml.cs => UpdateView.xaml.cs} | 4 +- .../WindsorInstallers/ServicesInstaller.cs | 14 +- .../WindsorInstallers/ViewModelsInstaller.cs | 6 +- Filtration/packages.config | 8 + 38 files changed, 1297 insertions(+), 432 deletions(-) create mode 100644 Filtration.Interface/packages.config create mode 100644 Filtration/Filtration.nuspec create mode 100644 Filtration/Services/Bootstrapper.cs create mode 100644 Filtration/Services/SettingsService.cs delete mode 100644 Filtration/Services/UpdateCheckService.cs create mode 100644 Filtration/Services/UpdateService.cs create mode 100644 Filtration/ViewModels/DesignTime/DesignTimeMainWindowViewModel.cs create mode 100644 Filtration/ViewModels/DesignTime/DesignTimeSettingsPageViewModel.cs create mode 100644 Filtration/ViewModels/DesignTime/DesignTimeUpdateViewModel.cs delete mode 100644 Filtration/ViewModels/UpdateAvailableViewModel.cs create mode 100644 Filtration/ViewModels/UpdateViewModel.cs delete mode 100644 Filtration/Views/UpdateAvailableView.xaml create mode 100644 Filtration/Views/UpdateTabView.xaml create mode 100644 Filtration/Views/UpdateTabView.xaml.cs create mode 100644 Filtration/Views/UpdateView.xaml rename Filtration/Views/{UpdateAvailableView.xaml.cs => UpdateView.xaml.cs} (55%) diff --git a/Filtration.Interface/Filtration.Interface.csproj b/Filtration.Interface/Filtration.Interface.csproj index bc71f57..d383624 100644 --- a/Filtration.Interface/Filtration.Interface.csproj +++ b/Filtration.Interface/Filtration.Interface.csproj @@ -31,9 +31,24 @@ 4 + + ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\GalaSoft.MvvmLight.dll + + + ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\GalaSoft.MvvmLight.Extras.dll + + + ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\GalaSoft.MvvmLight.Platform.dll + + + ..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll + + + ..\packages\MvvmLightLibs.5.3.0.0\lib\net45\System.Windows.Interactivity.dll + @@ -46,6 +61,9 @@ + + + + + + + + + + + + + + + + + ..\Releases\ + + --no-msi + (?<=\[assembly: AssemblyInformationalVersion\(").*?(?="\)\]) + @(ItemsFromFile) + $([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern))) + + + + + + + + + + \ No newline at end of file diff --git a/Filtration/Filtration.nuspec b/Filtration/Filtration.nuspec new file mode 100644 index 0000000..f89e215 --- /dev/null +++ b/Filtration/Filtration.nuspec @@ -0,0 +1,18 @@ + + + + Filtration + + 0.0.0.0 + Filtration + Ben Wallis + A Path of Exile loot filter script editor + false + Copyright 2018 + The release notes for 1.0.0 have not been written yet! + + + + + + \ No newline at end of file diff --git a/Filtration/NLog.config b/Filtration/NLog.config index 9c219f8..af4b9bb 100644 --- a/Filtration/NLog.config +++ b/Filtration/NLog.config @@ -10,6 +10,7 @@ layout="${longdate} ${uppercase:${level}} ${message}" /> + @@ -17,6 +18,7 @@ + \ No newline at end of file diff --git a/Filtration/Properties/AssemblyInfo.cs b/Filtration/Properties/AssemblyInfo.cs index cc32a0b..395dec4 100644 --- a/Filtration/Properties/AssemblyInfo.cs +++ b/Filtration/Properties/AssemblyInfo.cs @@ -10,7 +10,8 @@ using System.Runtime.CompilerServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("0.21")] +[assembly: AssemblyVersion("1.0.0")] +[assembly: AssemblyInformationalVersion("1.0.0-beta1")] [assembly: InternalsVisibleTo("Filtration.Tests")] [assembly: InternalsVisibleTo("Filtration.ItemFilterPreview.Tests")] diff --git a/Filtration/Properties/Settings.Designer.cs b/Filtration/Properties/Settings.Designer.cs index 49ad864..57a3ddc 100644 --- a/Filtration/Properties/Settings.Designer.cs +++ b/Filtration/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace Filtration.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -121,123 +121,111 @@ namespace Filtration.Properties { return ((string)(this["UpdateDataUrl"])); } } - + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool DownloadPrereleaseUpdates { + get { + return ((bool)(this["DownloadPrereleaseUpdates"])); + } + set { + this["DownloadPrereleaseUpdates"] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool ShowSectionBrowser - { - get - { + public bool ShowSectionBrowser { + get { return ((bool)(this["ShowSectionBrowser"])); } - set - { + set { this["ShowSectionBrowser"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool ShowBlockGroupBrowser - { - get - { + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ShowBlockGroupBrowser { + get { return ((bool)(this["ShowBlockGroupBrowser"])); } - set - { + set { this["ShowBlockGroupBrowser"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool ShowBlockOutputPreview - { - get - { + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ShowBlockOutputPreview { + get { return ((bool)(this["ShowBlockOutputPreview"])); } - set - { + set { this["ShowBlockOutputPreview"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool ShowAdvanced - { - get - { + public bool ShowAdvanced { + get { return ((bool)(this["ShowAdvanced"])); } - set - { + set { this["ShowAdvanced"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("Normal")] - public global::System.Windows.WindowState WindowState - { - get - { + public global::System.Windows.WindowState WindowState { + get { return ((global::System.Windows.WindowState)(this["WindowState"])); } - set - { + set { this["WindowState"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("960")] - public int WindowWidth - { - get - { + [global::System.Configuration.DefaultSettingValueAttribute("1200")] + public int WindowWidth { + get { return ((int)(this["WindowWidth"])); } - set - { + set { this["WindowWidth"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("720")] - public int WindowHeight - { - get - { + [global::System.Configuration.DefaultSettingValueAttribute("800")] + public int WindowHeight { + get { return ((int)(this["WindowHeight"])); } - set - { + set { this["WindowHeight"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("")] - public string LastActiveDocument - { - get - { + public string LastActiveDocument { + get { return ((string)(this["LastActiveDocument"])); } - set - { + set { this["LastActiveDocument"] = value; } } diff --git a/Filtration/Properties/Settings.settings b/Filtration/Properties/Settings.settings index 79cafbc..802669b 100644 --- a/Filtration/Properties/Settings.settings +++ b/Filtration/Properties/Settings.settings @@ -29,5 +29,32 @@ http://ben-wallis.github.io/Filtration/filtration_version.xml + + False + + + True + + + True + + + True + + + False + + + Normal + + + 1200 + + + 800 + + + + \ No newline at end of file diff --git a/Filtration/Services/Bootstrapper.cs b/Filtration/Services/Bootstrapper.cs new file mode 100644 index 0000000..b79b58f --- /dev/null +++ b/Filtration/Services/Bootstrapper.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; +using System.Windows; +using Filtration.Views; +using NLog; + +namespace Filtration.Services +{ + internal interface IBootstrapper + { + Task GoAsync(); + } + + internal class Bootstrapper : IBootstrapper + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly IMainWindow _mainWindow; + private readonly ISettingsService _settingsService; + private readonly IUpdateService _updateService; + + public Bootstrapper(IMainWindow mainWindow, + ISettingsService settingsService, + IUpdateService updateService) + { + _mainWindow = mainWindow; + _settingsService = settingsService; + _updateService = updateService; + } + + public async Task GoAsync() + { + AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException; + + // Attempt to restore user settings - this is required because after an update any + // user settings will have been lost due to Squirrel changing the app directory + // with each update + _settingsService.RestoreSettings(); + + _mainWindow.Show(); + + await _updateService.CheckForUpdates(); + } + + private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + var exception = (Exception) e.ExceptionObject; + + Logger.Fatal(exception); + var exceptionMessage = exception.Message + Environment.NewLine + exception.StackTrace; + var innerException = exception.InnerException != null + ? exception.InnerException.Message + Environment.NewLine + + exception.InnerException.StackTrace + : string.Empty; + + MessageBox.Show(exceptionMessage + Environment.NewLine + innerException, "Unhandled Exception", MessageBoxButton.OK, MessageBoxImage.Error); + } + } +} diff --git a/Filtration/Services/SettingsService.cs b/Filtration/Services/SettingsService.cs new file mode 100644 index 0000000..ecfee9a --- /dev/null +++ b/Filtration/Services/SettingsService.cs @@ -0,0 +1,64 @@ +using System; +using System.Configuration; +using System.IO; +using System.Reflection; +using Filtration.Properties; +using NLog; + +namespace Filtration.Services +{ + internal interface ISettingsService + { + void BackupSettings(); + void RestoreSettings(); + } + + internal class SettingsService : ISettingsService + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public void BackupSettings() + { + var currentUserSettingsFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath; + var backupFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\..\\last.config"; + + try + { + File.Copy(currentUserSettingsFile, backupFile, overwrite: true); + } + catch (Exception e) + { + Logger.Error(e); + } + } + + public void RestoreSettings() + { + Settings.Default.Save(); + string currentUserSettingsFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath; + string backupFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\..\\last.config"; + + // Check if we have settings that we need to restore + if (!File.Exists(backupFile)) + { + // Nothing we need to do + return; + } + + try + { + // Overwrite the current user settings file with the backed up user settings file + File.Copy(backupFile, currentUserSettingsFile, overwrite: true); + + // Delete backup file + File.Delete(backupFile); + + Settings.Default.Reload(); + } + catch (Exception e) + { + Logger.Error(e); + } + } + } +} diff --git a/Filtration/Services/UpdateCheckService.cs b/Filtration/Services/UpdateCheckService.cs deleted file mode 100644 index cd94bdc..0000000 --- a/Filtration/Services/UpdateCheckService.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Windows; -using System.Xml.Serialization; -using Filtration.Models; -using Filtration.Properties; -using Filtration.ViewModels; -using Filtration.Views; -using NLog; - -namespace Filtration.Services -{ - internal interface IUpdateCheckService - { - void CheckForUpdates(); - } - - internal class UpdateCheckService : IUpdateCheckService - { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - private readonly IHTTPService _httpService; - private readonly IUpdateAvailableViewModel _updateAvailableViewModel; - - public UpdateCheckService(IHTTPService httpService, - IUpdateAvailableViewModel updateAvailableViewModel) - { - _httpService = httpService; - _updateAvailableViewModel = updateAvailableViewModel; - } - - public void CheckForUpdates() - { - var assemblyVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); - - try - { - var updateData = GetUpdateData(); - updateData.CurrentVersionMajorPart = assemblyVersion.FileMajorPart; - updateData.CurrentVersionMinorPart = assemblyVersion.FileMinorPart; - - if (updateData.LatestVersionMajorPart >= updateData.CurrentVersionMajorPart && - updateData.LatestVersionMinorPart > updateData.CurrentVersionMinorPart) - { - if (Settings.Default.SuppressUpdates == false || LatestVersionIsNewerThanSuppressedVersion(updateData)) - { - Settings.Default.SuppressUpdates = false; - Settings.Default.Save(); - - updateData.UpdateAvailable = true; - } - } - - if (updateData.StaticDataUpdatedDate > Settings.Default.StaticDataLastUpdated) - { - var result = MessageBox.Show("New static data files are available (Item Base Types and Item Classes). Do you wish to download them?", - "Static Data Update Available", MessageBoxButton.YesNo, MessageBoxImage.Question); - - if (result == MessageBoxResult.Yes) - { - try - { - UpdateStaticDataFiles(); - Settings.Default.StaticDataLastUpdated = DateTime.Now; - Settings.Default.Save(); - - MessageBox.Show("Static Data successfully updated!", "Update Success", MessageBoxButton.OK, MessageBoxImage.Information); - } - catch (Exception e) - { - Logger.Error(e); - MessageBox.Show($"An error occurred while updating the static data files {Environment.NewLine}{e.Message}", "Update Error", MessageBoxButton.OK, - MessageBoxImage.Error); - } - } - } - - if (updateData.UpdateAvailable) - { - var updateAvailableView = new UpdateAvailableView { DataContext = _updateAvailableViewModel }; - _updateAvailableViewModel.Initialise(updateData); - _updateAvailableViewModel.OnRequestClose += (s, e) => updateAvailableView.Close(); - updateAvailableView.ShowDialog(); - } - } - catch (Exception e) - { - Logger.Debug(e); - // We don't care if the update check fails, because it could fail for multiple reasons - // including the user blocking Filtration in their firewall. - } - } - - private static bool LatestVersionIsNewerThanSuppressedVersion(UpdateData updateData) - { - return Settings.Default.SuppressUpdatesUpToVersionMajorPart < updateData.LatestVersionMajorPart || - (Settings.Default.SuppressUpdatesUpToVersionMajorPart <= updateData.LatestVersionMajorPart && - Settings.Default.SuppressUpdatesUpToVersionMinorPart < updateData.LatestVersionMinorPart); - } - - private UpdateData GetUpdateData() - { - var updateXml = _httpService.GetContent(Settings.Default.UpdateDataUrl); - return DeserializeUpdateData(updateXml); - } - - private void UpdateStaticDataFiles() - { - var itemClassesStaticData = _httpService.GetContent(Settings.Default.ItemClassesStaticDataUrl); - var itemBaseTypesStaticData = _httpService.GetContent(Settings.Default.ItemBaseTypesStaticDataUrl); - - var appdatapath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\Filtration\"; - Directory.CreateDirectory(appdatapath); - - var itemClassesPath = appdatapath + "ItemClasses.txt"; - var itemBaseTypesPath = appdatapath + "ItemBaseTypes.txt"; - - File.WriteAllText(itemClassesPath, itemClassesStaticData); - File.WriteAllText(itemBaseTypesPath, itemBaseTypesStaticData); - } - - private UpdateData DeserializeUpdateData(string updateDataString) - { - var serializer = new XmlSerializer(typeof(UpdateData)); - object result; - - using (TextReader reader = new StringReader(updateDataString)) - { - result = serializer.Deserialize(reader); - } - - return result as UpdateData; - } - } -} diff --git a/Filtration/Services/UpdateService.cs b/Filtration/Services/UpdateService.cs new file mode 100644 index 0000000..b41e092 --- /dev/null +++ b/Filtration/Services/UpdateService.cs @@ -0,0 +1,258 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Filtration.Properties; +using NLog; +using Squirrel; + +namespace Filtration.Services +{ + public enum UpdateStatus + { + NoUpdateAvailable, + CheckingForUpdate, + UpdateAvailable, + Downloading, + ReadyToApplyUpdate, + Updating, + UpdateComplete, + Error + } + + internal class UpdateStatusChangedEventArgs : EventArgs + { + public UpdateStatusChangedEventArgs(UpdateStatus updateStatus) + { + UpdateStatus = updateStatus; + } + + public UpdateStatus UpdateStatus { get; } + } + + internal class UpdateProgressChangedEventArgs : EventArgs + { + public UpdateProgressChangedEventArgs(int progress) + { + Progress = progress; + } + + public int Progress { get; } + } + + internal interface IUpdateService + { + event EventHandler UpdateStatusChanged; + + event EventHandler UpdateProgressChanged; + + UpdateStatus UpdateStatus { get; } + + string LatestReleaseNotes { get; } + + string LatestReleaseVersion { get; } + + Task CheckForUpdates(); + + Task DownloadUpdatesAsync(); + + Task ApplyUpdatesAsync(); + + void RestartAfterUpdate(); + } + + internal class UpdateService : IUpdateService + { + private readonly ISettingsService _settingsService; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private const string _localUpdatePath = @"C:\Repos\Filtration\Releases"; + private ReleaseEntry _latestRelease; + private UpdateInfo _updates; + + private UpdateStatus _updateStatus; + + public UpdateService(ISettingsService settingsService) + { + _settingsService = settingsService; + + UpdateStatus = UpdateStatus.NoUpdateAvailable; + } + + public event EventHandler UpdateStatusChanged; + public event EventHandler UpdateProgressChanged; + + public UpdateStatus UpdateStatus + { + get => _updateStatus; + private set + { + _updateStatus = value; + UpdateStatusChanged?.Invoke(this, new UpdateStatusChangedEventArgs(value)); + } + } + + public string LatestReleaseNotes { get; private set; } + + public string LatestReleaseVersion { get; private set; } + + public async Task CheckForUpdates() + { + if (UpdateStatus != UpdateStatus.NoUpdateAvailable) + { + throw new InvalidOperationException(); + } + + + Logger.Debug("Checking for update..."); + UpdateStatus = UpdateStatus.CheckingForUpdate; + + try + { + bool downloadPrereleaseUpdates; + downloadPrereleaseUpdates = Settings.Default.DownloadPrereleaseUpdates; +#if DEBUG + downloadPrereleaseUpdates = true; +#endif + using (var mgr = await UpdateManager.GitHubUpdateManager("https://github.com/ben-wallis/Filtration", prerelease: downloadPrereleaseUpdates)) + { + _updates = await mgr.CheckForUpdate(progress: progress => UpdateProgressChanged?.Invoke(this, new UpdateProgressChangedEventArgs(progress))); + } + + // Local file update source for testing + //using (var mgr = new UpdateManager(_localUpdatePath)) + //{ + + // _updates = await mgr.CheckForUpdate(progress: progress => UpdateProgressChanged?.Invoke(this, new UpdateProgressChangedEventArgs(progress))); + //} + } + catch (Exception e) + { + Logger.Error(e); + UpdateStatus = UpdateStatus.Error; + return; + } + + + if (_updates.ReleasesToApply.Any()) + { + _latestRelease = _updates.ReleasesToApply.OrderBy(x => x.Version).Last(); + LatestReleaseVersion = _latestRelease.Version.ToString(); + + Logger.Debug($"Update found ({LatestReleaseVersion}), fetching release notes..."); + + try + { + var releaseNotes = _latestRelease.GetReleaseNotes(_localUpdatePath); + LatestReleaseNotes = ProcessReleaseNotes(releaseNotes); + } + catch (Exception e) + { + Logger.Error(e); + UpdateStatus = UpdateStatus.Error; + return; + } + + UpdateStatus = UpdateStatus.UpdateAvailable; + } + else + { + UpdateStatus = UpdateStatus.NoUpdateAvailable; + Logger.Debug("No update available"); + } + } + + private string ProcessReleaseNotes(string rawReleaseNotes) + { + var regex = new Regex(@"", RegexOptions.Singleline); + var matches = regex.Match(rawReleaseNotes); + if (matches.Success) + { + var releaseNotes = matches.Groups[1].Value; + return "" + releaseNotes + ""; + } + + return string.Empty; + } + + public async Task DownloadUpdatesAsync() + { + if (_updates == null || UpdateStatus != UpdateStatus.UpdateAvailable) + { + throw new InvalidOperationException(); + } + + UpdateStatus = UpdateStatus.Downloading; + + try + { + using (var updateManager = new UpdateManager(_localUpdatePath)) + { + await updateManager.DownloadReleases(_updates.ReleasesToApply, OnProgressChanged); + } + } + catch (Exception e) + { + Logger.Error(e); + UpdateStatus = UpdateStatus.Error; + return; + } + + UpdateStatus = UpdateStatus.ReadyToApplyUpdate; + } + + + public async Task ApplyUpdatesAsync() + { + if (UpdateStatus != UpdateStatus.ReadyToApplyUpdate) + { + throw new InvalidOperationException(); + } + + UpdateStatus = UpdateStatus.Updating; + + // Back up the current user settings before updating as updating using Squirrel + // wipes out user settings due to the application directory changing with each update + _settingsService.BackupSettings(); + + try + { + using (var updateManager = new UpdateManager(_localUpdatePath)) + { + await updateManager.ApplyReleases(_updates, OnProgressChanged); + } + } + catch (Exception e) + { + Logger.Error(e); + UpdateStatus = UpdateStatus.Error; + return; + } + + UpdateStatus = UpdateStatus.UpdateComplete; + } + + public void RestartAfterUpdate() + { + if (UpdateStatus != UpdateStatus.UpdateComplete) + { + throw new InvalidOperationException(); + } + + try + { + UpdateManager.RestartApp(); + } + catch (Exception e) + { + Logger.Error(e); + UpdateStatus = UpdateStatus.Error; + } + } + + private void OnProgressChanged(int progress) + { + UpdateProgressChanged?.Invoke(this, new UpdateProgressChangedEventArgs(progress)); + } + } +} diff --git a/Filtration/ViewModels/DesignTime/DesignTimeMainWindowViewModel.cs b/Filtration/ViewModels/DesignTime/DesignTimeMainWindowViewModel.cs new file mode 100644 index 0000000..a8cbfef --- /dev/null +++ b/Filtration/ViewModels/DesignTime/DesignTimeMainWindowViewModel.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.ViewModels.DesignTime +{ + public class DesignTimeMainWindowViewModel : IMainWindowViewModel + { + public RelayCommand OpenScriptCommand { get; } + public RelayCommand NewScriptCommand { get; } + + public int WindowWidth + { + get => 1200; set {} } + + public int WindowHeight + { + get => 800; set { } + } + + public IUpdateViewModel UpdateViewModel => new DesignTimeUpdateViewModel(); + + public Task CloseAllDocumentsAsync() + { + throw new NotImplementedException(); + } + + public Task OpenDroppedFilesAsync(List filenames) + { + throw new NotImplementedException(); + } + } +} diff --git a/Filtration/ViewModels/DesignTime/DesignTimeSettingsPageViewModel.cs b/Filtration/ViewModels/DesignTime/DesignTimeSettingsPageViewModel.cs new file mode 100644 index 0000000..a4ffea7 --- /dev/null +++ b/Filtration/ViewModels/DesignTime/DesignTimeSettingsPageViewModel.cs @@ -0,0 +1,23 @@ +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.ViewModels.DesignTime +{ + public class DesignTimeSettingsPageViewModel : ISettingsPageViewModel + { + public RelayCommand SetItemFilterScriptDirectoryCommand { get; } + + public string DefaultFilterDirectory + { + get => @"c:\users\longusernamehere\Documents\My Games\Path of Exile"; + set { } + } + + public bool ExtraLineBetweenBlocks + { + get => true; + set { } + } + + public bool DownloadPrereleaseUpdates { get; set; } + } +} diff --git a/Filtration/ViewModels/DesignTime/DesignTimeUpdateViewModel.cs b/Filtration/ViewModels/DesignTime/DesignTimeUpdateViewModel.cs new file mode 100644 index 0000000..fff23b1 --- /dev/null +++ b/Filtration/ViewModels/DesignTime/DesignTimeUpdateViewModel.cs @@ -0,0 +1,49 @@ +using System.Threading.Tasks; +using Filtration.Services; +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.ViewModels.DesignTime +{ + public class DesignTimeUpdateViewModel : IUpdateViewModel + { + public RelayCommand HideUpdateWindowCommand { get; } + + public RelayCommand NextStepCommand { get; } + + public UpdateStatus UpdateStatus => UpdateStatus.Updating; + + public string NextStepButtonText + { + get => "Downloading... (100%)"; set {} } + + public bool Visible => true; + + public bool IsInErrorState { get; } + + public string Version => "1.2.3"; + + public string ReleaseNotes => @"Added support for DisableDropSound filter block items + Added support for GemLevel filter block items + Added support for HasExplicitMod filter block items + Added support for StackSize filter block items + Added support for CustomAlertSound filter block items + Added support for MinimapIcon filter block items + Added support for MapTier filter block items + Added support for PlayEffect filter block items + Added theme support for several filter blocks + Added collapse/expand support for sections + Added support for performing actions on whole sections(copy/move/delete etc) + Added search box to Section Browser + Improved parsing to support different syntaxes + Small bugfixes"; + + public bool IsScript { get; } + public bool IsTheme { get; } + public Task Close() + { + throw new System.NotImplementedException(); + } + + public RelayCommand CloseCommand { get; } + } +} diff --git a/Filtration/ViewModels/MainWindowViewModel.cs b/Filtration/ViewModels/MainWindowViewModel.cs index be5f314..0fd0c99 100644 --- a/Filtration/ViewModels/MainWindowViewModel.cs +++ b/Filtration/ViewModels/MainWindowViewModel.cs @@ -34,6 +34,9 @@ namespace Filtration.ViewModels { RelayCommand OpenScriptCommand { get; } RelayCommand NewScriptCommand { get; } + int WindowWidth { get; set; } + int WindowHeight { get; set; } + IUpdateViewModel UpdateViewModel { get; } Task CloseAllDocumentsAsync(); Task OpenDroppedFilesAsync(List filenames); } @@ -63,7 +66,8 @@ namespace Filtration.ViewModels IThemeProvider themeProvider, IThemeService themeService, IMessageBoxService messageBoxService, - IClipboardService clipboardService) + IClipboardService clipboardService, + IUpdateViewModel updateViewModel) { _itemFilterScriptRepository = itemFilterScriptRepository; _itemFilterScriptTranslator = itemFilterScriptTranslator; @@ -74,6 +78,8 @@ namespace Filtration.ViewModels _themeService = themeService; _messageBoxService = messageBoxService; _clipboardService = clipboardService; + UpdateViewModel = updateViewModel; + _windowState = Settings.Default.WindowState; _windowWidth = Settings.Default.WindowWidth; _windowHeight = Settings.Default.WindowHeight; @@ -266,13 +272,15 @@ namespace Filtration.ViewModels public IAvalonDockWorkspaceViewModel AvalonDockWorkspaceViewModel => _avalonDockWorkspaceViewModel; public ISettingsPageViewModel SettingsPageViewModel { get; } + public IUpdateViewModel UpdateViewModel { get; } + public string WindowTitle { get { var assembly = Assembly.GetExecutingAssembly(); var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); - return "Filtration v" + fvi.FileMajorPart + "." + fvi.FileMinorPart; + return "Filtration v" + fvi.ProductVersion; } } diff --git a/Filtration/ViewModels/SettingsPageViewModel.cs b/Filtration/ViewModels/SettingsPageViewModel.cs index d30d731..53102b5 100644 --- a/Filtration/ViewModels/SettingsPageViewModel.cs +++ b/Filtration/ViewModels/SettingsPageViewModel.cs @@ -1,16 +1,21 @@ using System.IO; using System.Windows; using Filtration.Common.Services; -using Filtration.Common.ViewModels; using Filtration.Properties; using Filtration.Services; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.CommandWpf; +using Microsoft.WindowsAPICodePack.Dialogs; namespace Filtration.ViewModels { internal interface ISettingsPageViewModel { + RelayCommand SetItemFilterScriptDirectoryCommand { get; } + + string DefaultFilterDirectory { get; } + bool ExtraLineBetweenBlocks { get; set; } + bool DownloadPrereleaseUpdates { get; set; } } internal class SettingsPageViewModel : ViewModelBase, ISettingsPageViewModel @@ -22,31 +27,44 @@ namespace Filtration.ViewModels { _itemFilterPersistenceService = itemFilterPersistenceService; _messageBoxService = messageBoxService; - SaveCommand = new RelayCommand(OnSaveCommand); - - DefaultFilterDirectory = Settings.Default.DefaultFilterDirectory; - ExtraLineBetweenBlocks = Settings.Default.ExtraLineBetweenBlocks; - SuppressUpdateNotifications = Settings.Default.SuppressUpdates; + SetItemFilterScriptDirectoryCommand = new RelayCommand(OnSetItemFilterScriptDirectoryCommand); } - public RelayCommand SaveCommand { get; private set; } - public string DefaultFilterDirectory { get; set; } - public bool ExtraLineBetweenBlocks { get; set; } - public bool SuppressUpdateNotifications { get; set; } + public RelayCommand SetItemFilterScriptDirectoryCommand { get; } - private void OnSaveCommand() + public string DefaultFilterDirectory => Settings.Default.DefaultFilterDirectory; + + public bool ExtraLineBetweenBlocks { - try - { - _itemFilterPersistenceService.SetItemFilterScriptDirectory(DefaultFilterDirectory); + get => Settings.Default.ExtraLineBetweenBlocks; + set => Settings.Default.ExtraLineBetweenBlocks = value; + } - Settings.Default.ExtraLineBetweenBlocks = ExtraLineBetweenBlocks; - Settings.Default.SuppressUpdates = SuppressUpdateNotifications; - } - catch (DirectoryNotFoundException) + public bool DownloadPrereleaseUpdates + { + get => Settings.Default.DownloadPrereleaseUpdates; + set => Settings.Default.DownloadPrereleaseUpdates = value; + } + + private void OnSetItemFilterScriptDirectoryCommand() + { + using (var dialog = new CommonOpenFileDialog()) { - _messageBoxService.Show("Error", "The entered Default Filter Directory is invalid or does not exist.", - MessageBoxButton.OK, MessageBoxImage.Exclamation); + dialog.IsFolderPicker = true; + var result = dialog.ShowDialog(); + if (result == CommonFileDialogResult.Ok) + { + try + { + _itemFilterPersistenceService.SetItemFilterScriptDirectory(dialog.FileName); + RaisePropertyChanged(nameof(DefaultFilterDirectory)); + } + catch (DirectoryNotFoundException) + { + _messageBoxService.Show("Error", "The entered Default Filter Directory is invalid or does not exist.", + MessageBoxButton.OK, MessageBoxImage.Exclamation); + } + } } } } diff --git a/Filtration/ViewModels/StartPageViewModel.cs b/Filtration/ViewModels/StartPageViewModel.cs index aab59e4..6a896a3 100644 --- a/Filtration/ViewModels/StartPageViewModel.cs +++ b/Filtration/ViewModels/StartPageViewModel.cs @@ -12,7 +12,6 @@ namespace Filtration.ViewModels internal class StartPageViewModel : PaneViewModel, IStartPageViewModel { - public StartPageViewModel() { Title = "Start Page"; @@ -20,8 +19,8 @@ namespace Filtration.ViewModels NewScriptCommand = new RelayCommand(OnNewScriptCommand); } - public RelayCommand OpenScriptCommand { get; private set; } - public RelayCommand NewScriptCommand { get; private set; } + public RelayCommand OpenScriptCommand { get; } + public RelayCommand NewScriptCommand { get; } public bool IsScript => false; public bool IsTheme => false; @@ -31,6 +30,8 @@ namespace Filtration.ViewModels throw new System.NotImplementedException(); } + public RelayCommand CloseCommand { get; } + private static void OnOpenScriptCommand() { Messenger.Default.Send(new NotificationMessage("OpenScript")); diff --git a/Filtration/ViewModels/UpdateAvailableViewModel.cs b/Filtration/ViewModels/UpdateAvailableViewModel.cs deleted file mode 100644 index 90c0bf0..0000000 --- a/Filtration/ViewModels/UpdateAvailableViewModel.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Diagnostics; -using Filtration.Models; -using Filtration.Properties; -using GalaSoft.MvvmLight.CommandWpf; - -namespace Filtration.ViewModels -{ - internal interface IUpdateAvailableViewModel - { - event EventHandler OnRequestClose; - void Initialise(UpdateData updateData); - string CurrentVersion { get; } - string NewVersion { get; } - string ReleaseNotes { get; } - DateTime ReleaseDate { get; } - } - - internal class UpdateAvailableViewModel : IUpdateAvailableViewModel - { - private UpdateData _updateData; - private int _currentVersionMajorPart; - private int _currentVersionMinorPart; - - public UpdateAvailableViewModel() - { - DownloadCommand = new RelayCommand(OnDownloadCommand); - AskLaterCommand = new RelayCommand(OnAskLaterCommand); - NeverAskAgainCommand = new RelayCommand(OnNeverAskAgainCommand); - } - - public event EventHandler OnRequestClose; - - public RelayCommand NeverAskAgainCommand { get; private set; } - public RelayCommand AskLaterCommand { get; private set; } - public RelayCommand DownloadCommand { get; private set; } - - public void Initialise(UpdateData updateData) - { - _currentVersionMajorPart = updateData.CurrentVersionMajorPart; - _currentVersionMinorPart = updateData.CurrentVersionMinorPart; - _updateData = updateData; - } - - public string CurrentVersion => _currentVersionMajorPart + "." + _currentVersionMinorPart; - - public string NewVersion => _updateData.LatestVersionMajorPart + "." + _updateData.LatestVersionMinorPart; - - public string ReleaseNotes => _updateData.ReleaseNotes; - - public DateTime ReleaseDate => _updateData.ReleaseDate; - - private void OnDownloadCommand() - { - Process.Start(_updateData.DownloadUrl); - } - - private void OnNeverAskAgainCommand() - { - Settings.Default.SuppressUpdates = true; - Settings.Default.SuppressUpdatesUpToVersionMajorPart = _updateData.LatestVersionMajorPart; - Settings.Default.SuppressUpdatesUpToVersionMinorPart = _updateData.LatestVersionMinorPart; - Settings.Default.Save(); - CloseWindow(); - } - - private void OnAskLaterCommand() - { - CloseWindow(); - } - - private void CloseWindow() - { - OnRequestClose?.Invoke(this, new EventArgs()); - } - } - -} - diff --git a/Filtration/ViewModels/UpdateViewModel.cs b/Filtration/ViewModels/UpdateViewModel.cs new file mode 100644 index 0000000..6ae0edf --- /dev/null +++ b/Filtration/ViewModels/UpdateViewModel.cs @@ -0,0 +1,203 @@ +using System; +using System.Threading.Tasks; +using Filtration.Common.ViewModels; +using Filtration.Interface; +using Filtration.Services; +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.ViewModels +{ + public interface IUpdateViewModel : IDocument + { + RelayCommand HideUpdateWindowCommand { get; } + + RelayCommand NextStepCommand { get; } + + UpdateStatus UpdateStatus { get; } + + string ReleaseNotes { get; } + + string Version { get; } + + string NextStepButtonText { get; } + + bool Visible { get; } + bool IsInErrorState { get; } + } + + /// + /// This ViewModel is shared between UpdateView.xaml and UpdateTabView.xaml since they are both + /// parts of the same update process, first the user gets given the opportunity to download the + /// update from the UpdateView box and then once it is ready to install they click Update + /// and are taken to the UpdateTabView tab where they can see the changes and begin the installation + /// of the update. + /// + internal class UpdateViewModel : PaneViewModel, IUpdateViewModel + { + private readonly IAvalonDockWorkspaceViewModel _avalonDockWorkspaceViewModel; + + private readonly IUpdateService _updateService; + private string _nextStepButtonText; + private bool _visible; + private bool _updateTabShown; + + public UpdateViewModel(IAvalonDockWorkspaceViewModel avalonDockWorkspaceViewModel, + IUpdateService updateService) + { + _avalonDockWorkspaceViewModel = avalonDockWorkspaceViewModel; + _updateService = updateService; + + updateService.UpdateProgressChanged += UpdateServiceOnUpdateProgressChanged; + updateService.UpdateStatusChanged += UpdateServiceOnUpdateStatusChanged; + + + HideUpdateWindowCommand = new RelayCommand(OnHideUpdateWindowCommand, () => UpdateStatus == UpdateStatus.UpdateAvailable || UpdateStatus == UpdateStatus.Error); + NextStepCommand = new RelayCommand(async () => await OnNextStepCommandAsync(), () => NextStepCommandEnabled); + + Title = "Update Available"; + } + + public RelayCommand NextStepCommand { get; } + + public RelayCommand HideUpdateWindowCommand { get; } + + public UpdateStatus UpdateStatus => _updateService.UpdateStatus; + + public bool IsInErrorState => UpdateStatus == UpdateStatus.Error; + + public async Task Close() + { + await Task.FromResult(true); + } + + public RelayCommand CloseCommand { get; } + + public bool IsScript => false; + public bool IsTheme => false; + + public bool Visible + { + get => _visible; + private set + { + _visible = value; + RaisePropertyChanged(); + } + } + + public string ReleaseNotes => _updateService.LatestReleaseNotes; + + public string Version => _updateService.LatestReleaseVersion; + + public string NextStepButtonText + { + get => _nextStepButtonText; + set + { + _nextStepButtonText = value; + RaisePropertyChanged(); + } + } + + private bool NextStepCommandEnabled => UpdateStatus == UpdateStatus.UpdateAvailable || UpdateStatus == UpdateStatus.ReadyToApplyUpdate || UpdateStatus == UpdateStatus.UpdateComplete; + + private async Task OnNextStepCommandAsync() + { + switch (UpdateStatus) + { + case UpdateStatus.UpdateAvailable: + { + await _updateService.DownloadUpdatesAsync(); + break; + } + case UpdateStatus.ReadyToApplyUpdate: + { + if (!_updateTabShown) + { + // When the update has downloaded and is ready to apply, clicking the button + // closes the update popup and shows the update tab. + _avalonDockWorkspaceViewModel.AddDocument(this); + Visible = false; + _updateTabShown = true; + NextStepButtonText = "Update"; + } + else + { + await _updateService.ApplyUpdatesAsync(); + } + + break; + } + case UpdateStatus.UpdateComplete: + { + _updateService.RestartAfterUpdate(); + break; + } + } + } + + private void UpdateServiceOnUpdateStatusChanged(object sender, UpdateStatusChangedEventArgs e) + { + switch (UpdateStatus) + { + case UpdateStatus.UpdateAvailable: + { + Visible = true; + NextStepButtonText = "Download"; + RaisePropertyChanged(nameof(ReleaseNotes)); + RaisePropertyChanged(nameof(Version)); + break; + } + case UpdateStatus.Downloading: + { + HideUpdateWindowCommand.RaiseCanExecuteChanged(); + NextStepButtonText = "Downloading (0%)"; + break; + } + case UpdateStatus.ReadyToApplyUpdate: + { + NextStepButtonText = "Update Ready"; + break; + } + case UpdateStatus.Updating: + { + NextStepButtonText = "Updating (0%)"; + break; + } + case UpdateStatus.UpdateComplete: + { + NextStepButtonText = "Restart"; + break; + } + case UpdateStatus.Error: + { + NextStepButtonText = "Update Error - Check Logs"; + Visible = true; + break; + } + } + + NextStepCommand.RaiseCanExecuteChanged(); + RaisePropertyChanged(nameof(UpdateStatus)); + RaisePropertyChanged(nameof(IsInErrorState)); + + } + + private void UpdateServiceOnUpdateProgressChanged(object sender, UpdateProgressChangedEventArgs e) + { + if (UpdateStatus == UpdateStatus.Downloading) + { + NextStepButtonText = $"Downloading ({e.Progress}%)"; + } + else if (UpdateStatus == UpdateStatus.Updating) + { + NextStepButtonText = $"Updating ({e.Progress}%)"; + } + } + + private void OnHideUpdateWindowCommand() + { + Visible = false; + } + } +} diff --git a/Filtration/Views/AboutWindow.xaml.cs b/Filtration/Views/AboutWindow.xaml.cs index 0facfaa..0a1b2d1 100644 --- a/Filtration/Views/AboutWindow.xaml.cs +++ b/Filtration/Views/AboutWindow.xaml.cs @@ -26,7 +26,7 @@ namespace Filtration.Views { var assembly = Assembly.GetExecutingAssembly(); var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); - return "Version " + fvi.FileMajorPart + "." + fvi.FileMinorPart; + return "Version " + fvi.ProductVersion; } } } diff --git a/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml b/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml index fdd92e3..4cb5f4a 100644 --- a/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml +++ b/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml @@ -61,6 +61,11 @@ + + + + + diff --git a/Filtration/Views/AvalonDock/PanesTemplateSelector.cs b/Filtration/Views/AvalonDock/PanesTemplateSelector.cs index a256dda..d9f1d09 100644 --- a/Filtration/Views/AvalonDock/PanesTemplateSelector.cs +++ b/Filtration/Views/AvalonDock/PanesTemplateSelector.cs @@ -15,11 +15,10 @@ namespace Filtration.Views.AvalonDock public DataTemplate BlockOutputPreviewTemplate { get; set; } public DataTemplate StartPageTemplate { get; set; } public DataTemplate ThemeTemplate { get; set; } + public DataTemplate UpdateTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { - var itemAsLayoutContent = item as LayoutContent; - if (item is ItemFilterScriptViewModel) { return ItemFilterScriptTemplate; @@ -50,6 +49,11 @@ namespace Filtration.Views.AvalonDock return StartPageTemplate; } + if (item is IUpdateViewModel) + { + return UpdateTemplate; + } + return base.SelectTemplate(item, container); } } diff --git a/Filtration/Views/MainWindow.xaml b/Filtration/Views/MainWindow.xaml index 928e347..6da3899 100644 --- a/Filtration/Views/MainWindow.xaml +++ b/Filtration/Views/MainWindow.xaml @@ -10,8 +10,9 @@ xmlns:views="clr-namespace:Filtration.Views" xmlns:gif="http://wpfanimatedgif.codeplex.com" xmlns:utility="clr-namespace:Filtration.Utility" - mc:Ignorable="d" - d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel}" + xmlns:designTime="clr-namespace:Filtration.ViewModels.DesignTime" + mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800" + d:DataContext="{d:DesignInstance Type=designTime:DesignTimeMainWindowViewModel, IsDesignTimeCreatable=True}" Title="{Binding WindowTitle}" Height="{Binding WindowHeight, Mode=TwoWay}" Width="{Binding WindowWidth, Mode=TwoWay}" IsIconVisible="True" WindowState="{Binding WindowState}" Closing="MainWindow_OnClosing" Drop="MainWindow_OnDrop" AllowDrop="True"> @@ -166,6 +167,7 @@ + diff --git a/Filtration/Views/SettingsPageView.xaml b/Filtration/Views/SettingsPageView.xaml index a462640..23f8832 100644 --- a/Filtration/Views/SettingsPageView.xaml +++ b/Filtration/Views/SettingsPageView.xaml @@ -3,12 +3,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:viewModels="clr-namespace:Filtration.ViewModels" - mc:Ignorable="d" - d:DataContext="{d:DesignInstance Type=viewModels:SettingsPageViewModel}"> - + xmlns:designTime="clr-namespace:Filtration.ViewModels.DesignTime" + mc:Ignorable="d" d:DesignWidth="1200" + d:DataContext="{d:DesignInstance Type=designTime:DesignTimeSettingsPageViewModel, IsDesignTimeCreatable=True}"> - + @@ -18,7 +17,7 @@ - + @@ -28,22 +27,22 @@ - Default Filter Directory: - + Default Filter Directory: + + + + + + + + Add blank line between blocks when saving - Suppress update notifications for current version - + Download pre-release updates (use with caution) + - - - - - - diff --git a/Filtration/Views/UpdateAvailableView.xaml b/Filtration/Views/UpdateAvailableView.xaml deleted file mode 100644 index 76f58a5..0000000 --- a/Filtration/Views/UpdateAvailableView.xaml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Filtration/Views/UpdateTabView.xaml b/Filtration/Views/UpdateTabView.xaml new file mode 100644 index 0000000..eb0fea3 --- /dev/null +++ b/Filtration/Views/UpdateTabView.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Version + + + + + + + + diff --git a/Filtration/Views/UpdateAvailableView.xaml.cs b/Filtration/Views/UpdateView.xaml.cs similarity index 55% rename from Filtration/Views/UpdateAvailableView.xaml.cs rename to Filtration/Views/UpdateView.xaml.cs index 4274251..ac56223 100644 --- a/Filtration/Views/UpdateAvailableView.xaml.cs +++ b/Filtration/Views/UpdateView.xaml.cs @@ -1,8 +1,8 @@ namespace Filtration.Views { - public partial class UpdateAvailableView + public partial class UpdateView { - public UpdateAvailableView() + public UpdateView() { InitializeComponent(); } diff --git a/Filtration/WindsorInstallers/ServicesInstaller.cs b/Filtration/WindsorInstallers/ServicesInstaller.cs index d96e5a1..f736c1e 100644 --- a/Filtration/WindsorInstallers/ServicesInstaller.cs +++ b/Filtration/WindsorInstallers/ServicesInstaller.cs @@ -25,14 +25,24 @@ namespace Filtration.WindsorInstallers .LifeStyle.Singleton); container.Register( - Component.For() - .ImplementedBy() + Component.For() + .ImplementedBy() .LifeStyle.Singleton); container.Register( Component.For() .ImplementedBy() .LifeStyle.Singleton); + + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); + + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); } } } diff --git a/Filtration/WindsorInstallers/ViewModelsInstaller.cs b/Filtration/WindsorInstallers/ViewModelsInstaller.cs index 30afd6e..e4924af 100644 --- a/Filtration/WindsorInstallers/ViewModelsInstaller.cs +++ b/Filtration/WindsorInstallers/ViewModelsInstaller.cs @@ -68,9 +68,9 @@ namespace Filtration.WindsorInstallers .LifeStyle.Transient); container.Register( - Component.For() - .ImplementedBy() - .LifeStyle.Transient); + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); container.Register( Component.For().AsFactory()); diff --git a/Filtration/packages.config b/Filtration/packages.config index d24e04a..0b98c5b 100644 --- a/Filtration/packages.config +++ b/Filtration/packages.config @@ -5,13 +5,21 @@ + + + + + + + + \ No newline at end of file