Add simple auto-save feature

This commit is contained in:
azakhi 2019-02-28 00:29:22 +03:00
parent 444f09751a
commit 7d341eb11f
9 changed files with 166 additions and 11 deletions

View File

@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows.Data;
using Filtration.Enums;
namespace Filtration.Converters
{
public class IntToAutosaveIntervalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (AutosaveInterval)(int)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)(AutosaveInterval)value;
}
}
}

View File

@ -0,0 +1,20 @@
using System.ComponentModel;
namespace Filtration.Enums
{
internal enum AutosaveInterval
{
[Description("Never")]
Never = -1,
[Description("5 sec")]
FiveSec = 5 * 1000,
[Description("10 sec")]
TenSec = 10 * 1000,
[Description("30 sec")]
ThirtySec = 30 * 1000,
[Description("1 min")]
OneMin = 60 * 1000,
[Description("5 min")]
FiveMin = 5 * 60 * 1000
}
}

View File

@ -212,10 +212,12 @@
<Compile Include="Converters\BlockItemToRemoveEnabledVisibilityConverter.cs" /> <Compile Include="Converters\BlockItemToRemoveEnabledVisibilityConverter.cs" />
<Compile Include="Converters\DisabledDefaultSoundConverter.cs" /> <Compile Include="Converters\DisabledDefaultSoundConverter.cs" />
<Compile Include="Converters\DisabledDefaultSoundTooltipConverter.cs" /> <Compile Include="Converters\DisabledDefaultSoundTooltipConverter.cs" />
<Compile Include="Converters\AutosaveIntervalConverter.cs" />
<Compile Include="Converters\MinimapIconToCroppedBitmapConverter.cs" /> <Compile Include="Converters\MinimapIconToCroppedBitmapConverter.cs" />
<Compile Include="Converters\HashSignRemovalConverter.cs" /> <Compile Include="Converters\HashSignRemovalConverter.cs" />
<Compile Include="Converters\ItemRarityConverter.cs" /> <Compile Include="Converters\ItemRarityConverter.cs" />
<Compile Include="Converters\TreeViewMarginConverter.cs" /> <Compile Include="Converters\TreeViewMarginConverter.cs" />
<Compile Include="Enums\AutosaveInterval.cs" />
<Compile Include="Enums\UpdateSource.cs" /> <Compile Include="Enums\UpdateSource.cs" />
<Compile Include="Models\UpdateData.cs" /> <Compile Include="Models\UpdateData.cs" />
<Compile Include="Properties\Annotations.cs" /> <Compile Include="Properties\Annotations.cs" />

View File

@ -166,5 +166,20 @@ namespace Filtration.Properties {
this["BlocksExpandedOnOpen"] = value; this["BlocksExpandedOnOpen"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("60000")]
public int AutosaveInterval
{
get
{
return ((int)(this["AutosaveInterval"]));
}
set
{
this["AutosaveInterval"] = value;
}
}
} }
} }

View File

@ -21,5 +21,8 @@ namespace Filtration.ViewModels.DesignTime
} }
public bool DownloadPrereleaseUpdates { get; set; } public bool DownloadPrereleaseUpdates { get; set; }
public int AutosaveInterval { get; set; }
public void SetAutosaveTimer (System.Timers.Timer timer) { }
} }
} }

View File

@ -43,6 +43,8 @@ namespace Filtration.ViewModels
bool ShowAdvanced { get; } bool ShowAdvanced { get; }
string Description { get; set; } string Description { get; set; }
string DisplayName { get; } string DisplayName { get; }
bool IsFilenameFake { get; }
bool Autosave { get; }
void Initialise(IItemFilterScript itemFilterScript, bool newScript); void Initialise(IItemFilterScript itemFilterScript, bool newScript);
void RemoveDirtyFlag(); void RemoveDirtyFlag();
@ -118,6 +120,9 @@ namespace Filtration.ViewModels
private List<IDisposable> _subscriptions; private List<IDisposable> _subscriptions;
private ObservableCollection<string> _customSoundsAvailable; private ObservableCollection<string> _customSoundsAvailable;
private readonly List<IItemFilterBlockViewModelBase> _lastAddedBlocks; private readonly List<IItemFilterBlockViewModelBase> _lastAddedBlocks;
private List<ObjectModel.ThemeEditor.ThemeComponent> _promptedThemeComponents;
public bool Autosave { get; private set; }
public ItemFilterScriptViewModel(IItemFilterBlockBaseViewModelFactory itemFilterBlockBaseViewModelFactory, public ItemFilterScriptViewModel(IItemFilterBlockBaseViewModelFactory itemFilterBlockBaseViewModelFactory,
IItemFilterBlockTranslator blockTranslator, IItemFilterBlockTranslator blockTranslator,
@ -138,6 +143,8 @@ namespace Filtration.ViewModels
_subscriptions = new List<IDisposable>(); _subscriptions = new List<IDisposable>();
ItemFilterBlockViewModels = new ObservableCollection<IItemFilterBlockViewModelBase>(); ItemFilterBlockViewModels = new ObservableCollection<IItemFilterBlockViewModelBase>();
SelectedBlockViewModels = new ObservableCollection<IItemFilterBlockViewModelBase>(); SelectedBlockViewModels = new ObservableCollection<IItemFilterBlockViewModelBase>();
_promptedThemeComponents = new List<ObjectModel.ThemeEditor.ThemeComponent>();
Autosave = true;
_subscriptions.Add( _subscriptions.Add(
SelectedBlockViewModels.ToObservableChangeSet() SelectedBlockViewModels.ToObservableChangeSet()
@ -218,7 +225,7 @@ namespace Filtration.ViewModels
Script.ItemFilterBlocks.CollectionChanged += ItemFilterBlocksOnCollectionChanged; Script.ItemFilterBlocks.CollectionChanged += ItemFilterBlocksOnCollectionChanged;
_filenameIsFake = newScript; IsFilenameFake = newScript;
if (newScript) if (newScript)
{ {
@ -653,7 +660,7 @@ namespace Filtration.ViewModels
public string Filepath => Script.FilePath; public string Filepath => Script.FilePath;
private bool _filenameIsFake; public bool IsFilenameFake { get; private set; }
private bool _showAdvanced; private bool _showAdvanced;
public async Task SaveAsync() public async Task SaveAsync()
@ -661,7 +668,7 @@ namespace Filtration.ViewModels
if (!ValidateScript()) return; if (!ValidateScript()) return;
if (!CheckForUnusedThemeComponents()) return; if (!CheckForUnusedThemeComponents()) return;
if (_filenameIsFake) if (IsFilenameFake)
{ {
await SaveAsAsync(); await SaveAsAsync();
return; return;
@ -672,6 +679,8 @@ namespace Filtration.ViewModels
{ {
await _persistenceService.SaveItemFilterScriptAsync(Script); await _persistenceService.SaveItemFilterScriptAsync(Script);
RemoveDirtyFlag(); RemoveDirtyFlag();
// Restore Autosave only when user successfully saves
Autosave = true;
} }
catch (Exception e) catch (Exception e)
{ {
@ -712,9 +721,11 @@ namespace Filtration.ViewModels
{ {
Script.FilePath = saveDialog.FileName; Script.FilePath = saveDialog.FileName;
await _persistenceService.SaveItemFilterScriptAsync(Script); await _persistenceService.SaveItemFilterScriptAsync(Script);
_filenameIsFake = false; IsFilenameFake = false;
Title = Filename; Title = Filename;
RemoveDirtyFlag(); RemoveDirtyFlag();
// Restore Autosave only when user successfully saves
Autosave = true;
} }
catch (Exception e) catch (Exception e)
{ {
@ -741,16 +752,37 @@ namespace Filtration.ViewModels
Script.ItemFilterBlocks.OfType<ItemFilterBlock>().Count( Script.ItemFilterBlocks.OfType<ItemFilterBlock>().Count(
b => b.BlockItems.OfType<IBlockItemWithTheme>().Count(i => i.ThemeComponent == t) > 0) == 0).ToList(); b => b.BlockItems.OfType<IBlockItemWithTheme>().Count(i => i.ThemeComponent == t) > 0) == 0).ToList();
// Do not prompt same components again
for (var i = _promptedThemeComponents.Count - 1; i >= 0; i--)
{
if (unusedThemeComponents.Contains(_promptedThemeComponents[i]))
{
unusedThemeComponents.Remove(_promptedThemeComponents[i]);
}
else
{
_promptedThemeComponents.RemoveAt(i);
}
}
if (unusedThemeComponents.Count <= 0) return true; if (unusedThemeComponents.Count <= 0) return true;
var themeComponents = unusedThemeComponents.Aggregate(string.Empty, var themeComponents = unusedThemeComponents.Aggregate(string.Empty,
(current, themeComponent) => current + themeComponent.ComponentName + Environment.NewLine); (current, themeComponent) => current + themeComponent.ComponentName + Environment.NewLine);
// Do not auto-save while user is prompted
Autosave = false;
var ignoreUnusedThemeComponents = _messageBoxService.Show("Unused Theme Components", var ignoreUnusedThemeComponents = _messageBoxService.Show("Unused Theme Components",
"The following theme components are unused, they will be lost when this script is reopened. Save anyway?" + "The following theme components are unused, they will be lost when this script is reopened. Save anyway?" +
Environment.NewLine + Environment.NewLine + themeComponents, MessageBoxButton.YesNo, Environment.NewLine + Environment.NewLine + themeComponents, MessageBoxButton.YesNo,
MessageBoxImage.Exclamation); MessageBoxImage.Exclamation);
if (ignoreUnusedThemeComponents == MessageBoxResult.Yes)
{
_promptedThemeComponents.AddRange(unusedThemeComponents);
}
return ignoreUnusedThemeComponents != MessageBoxResult.No; return ignoreUnusedThemeComponents != MessageBoxResult.No;
} }

View File

@ -210,6 +210,24 @@ namespace Filtration.ViewModels
} }
}); });
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += (sender, e) => OnTimedEvent(sender, e, this);
timer.Interval = Settings.Default.AutosaveInterval > 0 ? Settings.Default.AutosaveInterval : double.MaxValue;
timer.Enabled = Settings.Default.AutosaveInterval > 0;
settingsPageViewModel.SetAutosaveTimer(timer);
}
private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e, MainWindowViewModel mainWindowViewModel)
{
mainWindowViewModel.AutoSave();
}
public void AutoSave()
{
foreach (var doc in _avalonDockWorkspaceViewModel.OpenDocuments.OfType<IItemFilterScriptViewModel>().Where(doc => doc.IsDirty && doc.IsScript && !doc.IsFilenameFake && doc.Autosave))
{
doc.SaveAsync();
}
} }
public RelayCommand OpenScriptCommand { get; } public RelayCommand OpenScriptCommand { get; }

View File

@ -13,11 +13,15 @@ namespace Filtration.ViewModels
bool BlocksExpandedOnOpen { get; set; } bool BlocksExpandedOnOpen { get; set; }
bool DownloadPrereleaseUpdates { get; set; } bool DownloadPrereleaseUpdates { get; set; }
bool ExtraLineBetweenBlocks { get; set; } bool ExtraLineBetweenBlocks { get; set; }
int AutosaveInterval { get; set; }
void SetAutosaveTimer(System.Timers.Timer timer);
} }
internal class SettingsPageViewModel : ViewModelBase, ISettingsPageViewModel internal class SettingsPageViewModel : ViewModelBase, ISettingsPageViewModel
{ {
private readonly IItemFilterScriptDirectoryService _itemFilterScriptDirectoryService; private readonly IItemFilterScriptDirectoryService _itemFilterScriptDirectoryService;
private System.Timers.Timer _autosaveTimer;
public SettingsPageViewModel(IItemFilterScriptDirectoryService itemFilterScriptDirectoryService) public SettingsPageViewModel(IItemFilterScriptDirectoryService itemFilterScriptDirectoryService)
{ {
@ -47,6 +51,36 @@ namespace Filtration.ViewModels
set => Settings.Default.ExtraLineBetweenBlocks = value; set => Settings.Default.ExtraLineBetweenBlocks = value;
} }
public int AutosaveInterval
{
get => Settings.Default.AutosaveInterval;
set
{
Settings.Default.AutosaveInterval = value;
if (_autosaveTimer != null)
{
if (value < 0)
{
_autosaveTimer.Stop();
}
else if (_autosaveTimer.Interval != value)
{
_autosaveTimer.Stop();
_autosaveTimer.Interval = value;
_autosaveTimer.Start();
}
}
}
}
public void SetAutosaveTimer(System.Timers.Timer timer)
{
if (_autosaveTimer == null)
{
_autosaveTimer = timer;
}
}
private void OnSetItemFilterScriptDirectoryCommand() private void OnSetItemFilterScriptDirectoryCommand()
{ {
_itemFilterScriptDirectoryService.SetItemFilterScriptDirectory(); _itemFilterScriptDirectoryService.SetItemFilterScriptDirectory();

View File

@ -1,11 +1,17 @@
<UserControl x:Class="Filtration.Views.SettingsPageView" <UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:designTime="clr-namespace:Filtration.ViewModels.DesignTime" xmlns:designTime="clr-namespace:Filtration.ViewModels.DesignTime"
xmlns:extensions="clr-namespace:Filtration.Common.Extensions;assembly=Filtration.Common"
xmlns:enums="clr-namespace:Filtration.Enums"
xmlns:Converters="clr-namespace:Filtration.Converters" x:Class="Filtration.Views.SettingsPageView"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="400" mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="400"
d:DataContext="{d:DesignInstance Type=designTime:DesignTimeSettingsPageViewModel, IsDesignTimeCreatable=True}"> d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type={x:Type designTime:DesignTimeSettingsPageViewModel}}">
<UserControl.Resources>
<Converters:IntToAutosaveIntervalConverter x:Key="IntToAutosaveIntervalConverter"/>
</UserControl.Resources>
<Border BorderBrush="Black" BorderThickness="1"> <Border BorderBrush="Black" BorderThickness="1">
<DockPanel Margin="10" MaxWidth="600" HorizontalAlignment="Left"> <DockPanel Margin="10" MaxWidth="600" HorizontalAlignment="Left">
<Grid DockPanel.Dock="Bottom"> <Grid DockPanel.Dock="Bottom">
@ -27,7 +33,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">Default Filter Directory:</TextBlock> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"><Run Text="Default Filter Directory:"/></TextBlock>
<Grid Grid.Row="0" Grid.Column="2" Height="40" Width="300"> <Grid Grid.Row="0" Grid.Column="2" Height="40" Width="300">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@ -35,15 +41,20 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding DefaultFilterDirectory}" TextWrapping="Wrap" VerticalAlignment="Center" /> <TextBlock Grid.Column="0" Text="{Binding DefaultFilterDirectory}" TextWrapping="Wrap" VerticalAlignment="Center" />
<Button Grid.Column="1" Margin="5,0,0,0" Width="30" Height="30" Command="{Binding SetItemFilterScriptDirectoryCommand}"> <Button Grid.Column="1" Margin="5,0,0,0" Width="30" Height="30" Command="{Binding SetItemFilterScriptDirectoryCommand}">
<ContentControl Content="{StaticResource OpenIcon}" Width="16" Height="16"></ContentControl> <ContentControl Content="{StaticResource OpenIcon}" Width="16" Height="16"/>
</Button> </Button>
</Grid> </Grid>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center">Add blank line between blocks when saving</TextBlock> <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"><Run Text="Add blank line between blocks when saving"/></TextBlock>
<CheckBox Grid.Row="2" Grid.Column="2" IsChecked="{Binding ExtraLineBetweenBlocks}" /> <CheckBox Grid.Row="2" Grid.Column="2" IsChecked="{Binding ExtraLineBetweenBlocks}" />
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center">Download pre-release updates (use with caution)</TextBlock> <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center"><Run Text="Download pre-release updates (use with caution)"/></TextBlock>
<CheckBox Grid.Row="3" Grid.Column="2" IsChecked="{Binding DownloadPrereleaseUpdates}" /> <CheckBox Grid.Row="3" Grid.Column="2" IsChecked="{Binding DownloadPrereleaseUpdates}" />
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center">Auto-expand all sections when opening scripts</TextBlock> <TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center"><Run Text="Auto-expand all sections when opening scripts"/></TextBlock>
<CheckBox Grid.Row="4" Grid.Column="2" IsChecked="{Binding BlocksExpandedOnOpen}" /> <CheckBox Grid.Row="4" Grid.Column="2" IsChecked="{Binding BlocksExpandedOnOpen}" />
<TextBlock Grid.Row="5" Grid.Column="0" VerticalAlignment="Center"><Run Text="Auto-save interval"/></TextBlock>
<ComboBox Grid.Row="5" Grid.Column="2" ItemsSource="{Binding Source={extensions:Enumeration {x:Type enums:AutosaveInterval}}}"
DisplayMemberPath="Description"
SelectedValue="{Binding AutosaveInterval, Converter={StaticResource IntToAutosaveIntervalConverter}}"
SelectedValuePath="Value" />
</Grid> </Grid>
</Grid> </Grid>
</DockPanel> </DockPanel>