mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
FilePickerVM unit tests, better filter enforcement
This commit is contained in:
parent
7c03806e9a
commit
078b457857
38
Wabbajack.Common/Util/TempFile.cs
Normal file
38
Wabbajack.Common/Util/TempFile.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
public class TempFile : IDisposable
|
||||
{
|
||||
public FileInfo File { get; private set; }
|
||||
public bool DeleteAfter = true;
|
||||
|
||||
public TempFile(bool deleteAfter = true, bool createFolder = true)
|
||||
: this(new FileInfo(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())))
|
||||
{
|
||||
}
|
||||
|
||||
public TempFile(FileInfo file, bool deleteAfter = true, bool createFolder = true)
|
||||
{
|
||||
this.File = file;
|
||||
if (createFolder && !file.Directory.Exists)
|
||||
{
|
||||
file.Directory.Create();
|
||||
}
|
||||
this.DeleteAfter = deleteAfter;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (DeleteAfter)
|
||||
{
|
||||
this.File.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
Wabbajack.Common/Util/TempFolder.cs
Normal file
45
Wabbajack.Common/Util/TempFolder.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
public class TempFolder : IDisposable
|
||||
{
|
||||
public DirectoryInfo Dir { get; private set; }
|
||||
public bool DeleteAfter = true;
|
||||
|
||||
public TempFolder(bool deleteAfter = true)
|
||||
{
|
||||
this.Dir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
this.Dir.Create();
|
||||
this.DeleteAfter = deleteAfter;
|
||||
}
|
||||
|
||||
public TempFolder(DirectoryInfo dir, bool deleteAfter = true)
|
||||
{
|
||||
this.Dir = dir;
|
||||
if (!dir.Exists)
|
||||
{
|
||||
this.Dir.Create();
|
||||
}
|
||||
this.DeleteAfter = deleteAfter;
|
||||
}
|
||||
|
||||
public TempFolder(string addedFolderPath, bool deleteAfter = true)
|
||||
: this(new DirectoryInfo(Path.Combine(Path.GetTempPath(), addedFolderPath)), deleteAfter: deleteAfter)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (DeleteAfter)
|
||||
{
|
||||
Utils.DeleteDirectory(this.Dir.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -75,9 +75,6 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Syroot.KnownFolders">
|
||||
<HintPath>..\..\..\Users\tbald\.nuget\packages\syroot.windows.io.knownfolders\1.2.1\lib\net452\Syroot.KnownFolders.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
@ -103,6 +100,7 @@
|
||||
<Compile Include="Enums\ModManager.cs" />
|
||||
<Compile Include="ExtensionManager.cs" />
|
||||
<Compile Include="Extensions\DictionaryExt.cs" />
|
||||
<Compile Include="Extensions\EnumerableExt.cs" />
|
||||
<Compile Include="Extensions\EnumExt.cs" />
|
||||
<Compile Include="Extensions\HashHelper.cs" />
|
||||
<Compile Include="Extensions\RxExt.cs" />
|
||||
@ -130,6 +128,8 @@
|
||||
<Compile Include="StatusUpdate.cs" />
|
||||
<Compile Include="SteamHandler.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
<Compile Include="Util\TempFile.cs" />
|
||||
<Compile Include="Util\TempFolder.cs" />
|
||||
<Compile Include="WorkQueue.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
3
Wabbajack.Lib/FodyWeavers.xml
Normal file
3
Wabbajack.Lib/FodyWeavers.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ReactiveUI />
|
||||
</Weavers>
|
26
Wabbajack.Lib/FodyWeavers.xsd
Normal file
26
Wabbajack.Lib/FodyWeavers.xsd
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
|
||||
<xs:element name="Weavers">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="ReactiveUI" minOccurs="0" maxOccurs="1" type="xs:anyType" />
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="GenerateXsd" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
@ -1,4 +1,5 @@
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using DynamicData;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System;
|
||||
@ -9,7 +10,7 @@ using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
namespace Wabbajack
|
||||
namespace Wabbajack.Lib
|
||||
{
|
||||
public class FilePickerVM : ViewModel
|
||||
{
|
||||
@ -21,10 +22,10 @@ namespace Wabbajack
|
||||
Folder
|
||||
}
|
||||
|
||||
public enum ExistCheckOptions
|
||||
public enum CheckOptions
|
||||
{
|
||||
Off,
|
||||
IfNotEmpty,
|
||||
IfPathNotEmpty,
|
||||
On
|
||||
}
|
||||
|
||||
@ -43,7 +44,10 @@ namespace Wabbajack
|
||||
public PathTypeOptions PathType { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ExistCheckOptions ExistCheckOption { get; set; }
|
||||
public CheckOptions ExistCheckOption { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public CheckOptions FilterCheckOption { get; set; } = CheckOptions.IfPathNotEmpty;
|
||||
|
||||
[Reactive]
|
||||
public IObservable<IErrorResponse> AdditionalError { get; set; }
|
||||
@ -60,46 +64,49 @@ namespace Wabbajack
|
||||
private readonly ObservableAsPropertyHelper<string> _errorTooltip;
|
||||
public string ErrorTooltip => _errorTooltip.Value;
|
||||
|
||||
public List<CommonFileDialogFilter> Filters { get; } = new List<CommonFileDialogFilter>();
|
||||
public SourceList<CommonFileDialogFilter> Filters { get; } = new SourceList<CommonFileDialogFilter>();
|
||||
|
||||
public FilePickerVM(object parentVM = null)
|
||||
{
|
||||
Parent = parentVM;
|
||||
SetTargetPathCommand = ConstructTypicalPickerCommand();
|
||||
|
||||
// Check that file exists
|
||||
|
||||
var existsCheckTuple = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ExistCheckOption),
|
||||
this.WhenAny(x => x.PathType),
|
||||
this.WhenAny(x => x.TargetPath)
|
||||
// Dont want to debounce the initial value, because we know it's null
|
||||
.Skip(1)
|
||||
.Debounce(TimeSpan.FromMilliseconds(200))
|
||||
.StartWith(default(string)),
|
||||
// Dont want to debounce the initial value, because we know it's null
|
||||
.Skip(1)
|
||||
.Debounce(TimeSpan.FromMilliseconds(200))
|
||||
.StartWith(default(string)),
|
||||
resultSelector: (existsOption, type, path) => (ExistsOption: existsOption, Type: type, Path: path))
|
||||
.Publish()
|
||||
.StartWith((ExistsOption: ExistCheckOption, Type: PathType, Path: TargetPath))
|
||||
.Replay(1)
|
||||
.RefCount();
|
||||
|
||||
var doExistsCheck = existsCheckTuple
|
||||
.Select(t =>
|
||||
{
|
||||
// Don't do exists type if we don't know what path type we're tracking
|
||||
if (t.Type == PathTypeOptions.Off) return false;
|
||||
switch (t.ExistsOption)
|
||||
{
|
||||
case CheckOptions.Off:
|
||||
return false;
|
||||
case CheckOptions.IfPathNotEmpty:
|
||||
return !string.IsNullOrWhiteSpace(t.Path);
|
||||
case CheckOptions.On:
|
||||
return true;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
})
|
||||
.Replay(1)
|
||||
.RefCount();
|
||||
|
||||
_exists = Observable.Interval(TimeSpan.FromSeconds(3))
|
||||
// Only check exists on timer if desired
|
||||
.FilterSwitch(existsCheckTuple
|
||||
.Select(t =>
|
||||
{
|
||||
// Don't do exists type if we don't know what path type we're tracking
|
||||
if (t.Type == PathTypeOptions.Off) return false;
|
||||
switch (t.ExistsOption)
|
||||
{
|
||||
case ExistCheckOptions.Off:
|
||||
return false;
|
||||
case ExistCheckOptions.IfNotEmpty:
|
||||
return !string.IsNullOrWhiteSpace(t.Path);
|
||||
case ExistCheckOptions.On:
|
||||
return true;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}))
|
||||
.FilterSwitch(doExistsCheck)
|
||||
.Unit()
|
||||
// Also check though, when fields change
|
||||
.Merge(this.WhenAny(x => x.PathType).Unit())
|
||||
@ -113,14 +120,14 @@ namespace Wabbajack
|
||||
{
|
||||
switch (t.ExistsOption)
|
||||
{
|
||||
case ExistCheckOptions.IfNotEmpty:
|
||||
if (string.IsNullOrWhiteSpace(t.Path)) return true;
|
||||
case CheckOptions.IfPathNotEmpty:
|
||||
if (string.IsNullOrWhiteSpace(t.Path)) return false;
|
||||
break;
|
||||
case ExistCheckOptions.On:
|
||||
case CheckOptions.On:
|
||||
break;
|
||||
case ExistCheckOptions.Off:
|
||||
case CheckOptions.Off:
|
||||
default:
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
switch (t.Type)
|
||||
{
|
||||
@ -130,24 +137,82 @@ namespace Wabbajack
|
||||
return File.Exists(t.Path);
|
||||
case PathTypeOptions.Folder:
|
||||
return Directory.Exists(t.Path);
|
||||
case PathTypeOptions.Off:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.DistinctUntilChanged()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.StartWith(false)
|
||||
.ToProperty(this, nameof(Exists));
|
||||
|
||||
var passesFilters = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.TargetPath),
|
||||
this.WhenAny(x => x.PathType),
|
||||
this.WhenAny(x => x.FilterCheckOption),
|
||||
Filters.Connect().QueryWhenChanged(),
|
||||
resultSelector: (target, type, checkOption, query) =>
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PathTypeOptions.Either:
|
||||
case PathTypeOptions.File:
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
if (query.Count == 0) return true;
|
||||
switch (checkOption)
|
||||
{
|
||||
case CheckOptions.Off:
|
||||
return true;
|
||||
case CheckOptions.IfPathNotEmpty:
|
||||
if (string.IsNullOrWhiteSpace(target)) return true;
|
||||
break;
|
||||
case CheckOptions.On:
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var extension = Path.GetExtension(target);
|
||||
if (extension == null || !extension.StartsWith(".")) return false;
|
||||
extension = extension.Substring(1);
|
||||
if (!query.Any(filter => filter.Extensions.Any(ext => string.Equals(ext, extension)))) return false;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.StartWith(false)
|
||||
.DistinctUntilChanged()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.ToProperty(this, nameof(Exists));
|
||||
.StartWith(true)
|
||||
.Select(passed =>
|
||||
{
|
||||
if (passed) return ErrorResponse.Success;
|
||||
return ErrorResponse.Fail($"Path does not pass designated filters");
|
||||
})
|
||||
.Publish()
|
||||
.RefCount();
|
||||
|
||||
_errorState = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Exists)
|
||||
this.WhenAny(x => x.Exists),
|
||||
Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Exists),
|
||||
doExistsCheck,
|
||||
resultSelector: (exists, doExists) => !doExists || exists)
|
||||
.Select(exists => ErrorResponse.Create(successful: exists, exists ? default(string) : "Path does not exist")),
|
||||
passesFilters,
|
||||
this.WhenAny(x => x.AdditionalError)
|
||||
.Select(x => x ?? Observable.Return<IErrorResponse>(ErrorResponse.Success))
|
||||
.Switch(),
|
||||
resultSelector: (exist, err) =>
|
||||
resultSelector: (exists, existCheck, filter, err) =>
|
||||
{
|
||||
if (exist.Failed) return exist;
|
||||
if (existCheck.Failed) return existCheck;
|
||||
if (filter.Failed) return filter;
|
||||
return ErrorResponse.Convert(err);
|
||||
})
|
||||
.ToProperty(this, nameof(ErrorState));
|
||||
@ -161,12 +226,15 @@ namespace Wabbajack
|
||||
_errorTooltip = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.Exists)
|
||||
.Select(exists => exists ? default(string) : "Path does not exist"),
|
||||
passesFilters
|
||||
.Select(x => x.Reason),
|
||||
this.WhenAny(x => x.AdditionalError)
|
||||
.Select(x => x ?? Observable.Return<IErrorResponse>(ErrorResponse.Success))
|
||||
.Switch(),
|
||||
resultSelector: (exists, err) =>
|
||||
resultSelector: (exists, filters, err) =>
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(exists)) return exists;
|
||||
if (!string.IsNullOrWhiteSpace(filters)) return filters;
|
||||
return err?.Reason;
|
||||
})
|
||||
.ToProperty(this, nameof(ErrorTooltip));
|
||||
@ -201,7 +269,7 @@ namespace Wabbajack
|
||||
Multiselect = false,
|
||||
ShowPlacesList = true,
|
||||
};
|
||||
foreach (var filter in Filters)
|
||||
foreach (var filter in Filters.Items)
|
||||
{
|
||||
dlg.Filters.Add(filter);
|
||||
}
|
@ -59,7 +59,6 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="Syroot.KnownFolders" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Design" />
|
||||
@ -119,6 +118,7 @@
|
||||
<Compile Include="Downloaders\GameFileSourceDownloader.cs" />
|
||||
<Compile Include="Downloaders\LoversLabDownloader.cs" />
|
||||
<Compile Include="Downloaders\SteamWorkshopDownloader.cs" />
|
||||
<Compile Include="Extensions\ReactiveUIExt.cs" />
|
||||
<Compile Include="LibCefHelpers\Init.cs" />
|
||||
<Compile Include="MO2Compiler.cs" />
|
||||
<Compile Include="Data.cs" />
|
||||
@ -145,6 +145,7 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ReportBuilder.cs" />
|
||||
<Compile Include="StatusMessages\ConfirmUpdateOfExistingInstall.cs" />
|
||||
<Compile Include="UI\FilePickerVM.cs" />
|
||||
<Compile Include="UI\UIUtils.cs" />
|
||||
<Compile Include="Validation\DTOs.cs" />
|
||||
<Compile Include="Validation\ValidateModlist.cs" />
|
||||
@ -210,6 +211,9 @@
|
||||
<PackageReference Include="ReactiveUI">
|
||||
<Version>11.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ReactiveUI.Fody">
|
||||
<Version>11.0.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SharpCompress">
|
||||
<Version>0.24.0</Version>
|
||||
</PackageReference>
|
||||
|
344
Wabbajack.Test/FilePickerTests.cs
Normal file
344
Wabbajack.Test/FilePickerTests.cs
Normal file
@ -0,0 +1,344 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using DynamicData;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
namespace Wabbajack.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class FilePickerTests
|
||||
{
|
||||
public static TempFile CreateSetFile(FilePickerVM vm)
|
||||
{
|
||||
var temp = new TempFile();
|
||||
using (new FileStream(temp.File.FullName, FileMode.CreateNew)) { }
|
||||
vm.TargetPath = temp.File.FullName;
|
||||
return temp;
|
||||
}
|
||||
|
||||
public static TempFolder CreateSetFolder(FilePickerVM vm)
|
||||
{
|
||||
var temp = new TempFolder();
|
||||
Directory.CreateDirectory(temp.Dir.FullName);
|
||||
vm.TargetPath = temp.Dir.FullName;
|
||||
return temp;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Stock()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
Assert.AreEqual(FilePickerVM.PathTypeOptions.Off, vm.PathType);
|
||||
Assert.AreEqual(FilePickerVM.CheckOptions.Off, vm.ExistCheckOption);
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileNoExistsCheck_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.Off;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileNoExistsCheck_Exists()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFile(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.Off;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ExistCheckTypeOff_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Off;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ExistCheckTypeOff_Exists()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFile(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Off;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileIfNotEmptyCheck_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileIfNotEmptyCheck_SetPath_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.TargetPath = "SomePath.jpg";
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileIfNotEmptyCheck_Exists()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFile(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty;
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileOnExistsCheck_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileOnExistsCheck_Exists()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFile(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FolderIfNotEmptyCheck_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Folder;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FolderIfNotEmptyCheck_SetPath_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Folder;
|
||||
vm.TargetPath = "SomePath.jpg";
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FolderIfNotEmptyCheck_Exists()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFolder(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Folder;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty;
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FolderOnExistsCheck_DoesNotExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Folder;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FolderOnExistsCheck_Exists()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFolder(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Folder;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task AdditionalError_Success()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.AdditionalError = Observable.Return<IErrorResponse>(ErrorResponse.Succeed());
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task AdditionalError_Fail()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.AdditionalError = Observable.Return<IErrorResponse>(ErrorResponse.Fail());
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileExistsButSetToFolder()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFile(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.Folder;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FolderExistsButSetToFile()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFolder(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.On;
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileWithFilters_Passes()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFile(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.Off;
|
||||
vm.Filters.Add(new Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialogFilter("test", $"*.{Path.GetExtension(vm.TargetPath)}"));
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileWithFilters_ExistsButFails()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
using (CreateSetFile(vm))
|
||||
{
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.Off;
|
||||
vm.Filters.Add(new Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialogFilter("test", $"*.{Path.GetExtension(vm.TargetPath)}z"));
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsFalse(vm.ErrorState.Succeeded);
|
||||
Assert.IsTrue(vm.InError);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileWithFilters_PassesButDoesntExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.Off;
|
||||
vm.TargetPath = "SomePath.png";
|
||||
vm.Filters.Add(new Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialogFilter("test", $"*.{Path.GetExtension(vm.TargetPath)}"));
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task FileWithFilters_IfNotEmptyCheck_DoesntExist()
|
||||
{
|
||||
var vm = new FilePickerVM();
|
||||
vm.PathType = FilePickerVM.PathTypeOptions.File;
|
||||
vm.ExistCheckOption = FilePickerVM.CheckOptions.Off;
|
||||
vm.FilterCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty;
|
||||
vm.Filters.Add(new Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialogFilter("test", $"*.{Path.GetExtension(vm.TargetPath)}"));
|
||||
await Task.Delay(250);
|
||||
Assert.IsFalse(vm.Exists);
|
||||
Assert.IsTrue(vm.ErrorState.Succeeded);
|
||||
Assert.IsFalse(vm.InError);
|
||||
}
|
||||
}
|
||||
}
|
@ -105,6 +105,7 @@
|
||||
<Compile Include="DownloaderTests.cs" />
|
||||
<Compile Include="EndToEndTests.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="FilePickerTests.cs" />
|
||||
<Compile Include="MiscTests.cs" />
|
||||
<Compile Include="ModlistMetadataTests.cs" />
|
||||
<Compile Include="TestUtils.cs" />
|
||||
|
@ -66,7 +66,7 @@ namespace Wabbajack
|
||||
|
||||
OutputLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.IfNotEmpty,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select the folder to place the resulting modlist.wabbajack file",
|
||||
};
|
||||
|
@ -43,13 +43,13 @@ namespace Wabbajack
|
||||
Parent = parent;
|
||||
ModlistLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.On,
|
||||
PathType = FilePickerVM.PathTypeOptions.File,
|
||||
PromptTitle = "Select modlist"
|
||||
};
|
||||
DownloadLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.On,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select download location",
|
||||
};
|
||||
@ -91,13 +91,44 @@ namespace Wabbajack
|
||||
return ErrorResponse.Fail($"MO2 Folder could not be located from the given modlist location.{Environment.NewLine}Make sure your modlist is inside a valid MO2 distribution.");
|
||||
});
|
||||
|
||||
// Load custom modlist settings per MO2 profile
|
||||
_modlistSettings = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModlistLocation.ErrorState),
|
||||
this.WhenAny(x => x.ModlistLocation.TargetPath),
|
||||
resultSelector: (state, path) => (State: state, Path: path))
|
||||
// A short throttle is a quick hack to make the above changes "atomic"
|
||||
.Throttle(TimeSpan.FromMilliseconds(25))
|
||||
.Select(u =>
|
||||
{
|
||||
if (u.State.Failed) return null;
|
||||
var modlistSettings = _settings.ModlistSettings.TryCreate(u.Path);
|
||||
return new ModlistSettingsEditorVM(modlistSettings)
|
||||
{
|
||||
ModListName = MOProfile
|
||||
};
|
||||
})
|
||||
// Interject and save old while loading new
|
||||
.Pairwise()
|
||||
.Do(pair =>
|
||||
{
|
||||
pair.Previous?.Save();
|
||||
pair.Current?.Init();
|
||||
})
|
||||
.Select(x => x.Current)
|
||||
// Save to property
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(ModlistSettings));
|
||||
|
||||
// Wire start command
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModlistLocation.InError),
|
||||
this.WhenAny(x => x.DownloadLocation.InError),
|
||||
parent.WhenAny(x => x.OutputLocation.InError),
|
||||
resultSelector: (ml, down, output) => !ml && !down && !output)
|
||||
this.WhenAny(x => x.ModlistSettings)
|
||||
.Select(x => x?.InError ?? Observable.Return(false))
|
||||
.Switch(),
|
||||
resultSelector: (ml, down, output, modlistSettings) => !ml && !down && !output && !modlistSettings)
|
||||
.ObserveOnGuiThread(),
|
||||
execute: async () =>
|
||||
{
|
||||
@ -161,34 +192,6 @@ namespace Wabbajack
|
||||
.Subscribe(_ => Unload())
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
// Load custom modlist settings per MO2 profile
|
||||
_modlistSettings = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ModlistLocation.ErrorState),
|
||||
this.WhenAny(x => x.ModlistLocation.TargetPath),
|
||||
resultSelector: (state, path) => (State: state, Path: path))
|
||||
// A short throttle is a quick hack to make the above changes "atomic"
|
||||
.Throttle(TimeSpan.FromMilliseconds(25))
|
||||
.Select(u =>
|
||||
{
|
||||
if (u.State.Failed) return null;
|
||||
var modlistSettings = _settings.ModlistSettings.TryCreate(u.Path);
|
||||
return new ModlistSettingsEditorVM(modlistSettings)
|
||||
{
|
||||
ModListName = MOProfile
|
||||
};
|
||||
})
|
||||
// Interject and save old while loading new
|
||||
.Pairwise()
|
||||
.Do(pair =>
|
||||
{
|
||||
pair.Previous?.Save();
|
||||
pair.Current?.Init();
|
||||
})
|
||||
.Select(x => x.Current)
|
||||
// Save to property
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(ModlistSettings));
|
||||
|
||||
// If Mo2 folder changes and download location is empty, set it for convenience
|
||||
this.WhenAny(x => x.Mo2Folder)
|
||||
.DelayInitial(TimeSpan.FromMilliseconds(100))
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using DynamicData;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
@ -24,27 +27,30 @@ namespace Wabbajack
|
||||
[Reactive]
|
||||
public string Website { get; set; }
|
||||
|
||||
public IObservable<bool> InError { get; }
|
||||
|
||||
public ModlistSettingsEditorVM(CompilationModlistSettings settings)
|
||||
{
|
||||
this._settings = settings;
|
||||
ImagePath = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.IfNotEmpty,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty,
|
||||
PathType = FilePickerVM.PathTypeOptions.File,
|
||||
Filters =
|
||||
{
|
||||
new CommonFileDialogFilter("Banner image", "*.png")
|
||||
}
|
||||
};
|
||||
ImagePath.Filters.Add(new CommonFileDialogFilter("Banner image", "*.png"));
|
||||
ReadMeText = new FilePickerVM()
|
||||
{
|
||||
PathType = FilePickerVM.PathTypeOptions.File,
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.IfNotEmpty,
|
||||
Filters =
|
||||
{
|
||||
new CommonFileDialogFilter("Text", "*.txt"),
|
||||
}
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.IfPathNotEmpty,
|
||||
};
|
||||
ReadMeText.Filters.Add(new CommonFileDialogFilter("Text", "*.txt"));
|
||||
|
||||
InError = Observable.CombineLatest(
|
||||
this.WhenAny(x => x.ImagePath.ErrorState).Select(err => err.Failed),
|
||||
this.WhenAny(x => x.ReadMeText.ErrorState).Select(err => err.Failed),
|
||||
resultSelector: (img, readme) => img || readme)
|
||||
.Publish()
|
||||
.RefCount();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
|
@ -59,30 +59,53 @@ namespace Wabbajack
|
||||
Parent = parent;
|
||||
GameLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.On,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select Game Folder Location"
|
||||
};
|
||||
DownloadsLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.On,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select Downloads Folder"
|
||||
};
|
||||
StagingLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.On,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select Staging Folder"
|
||||
};
|
||||
|
||||
// Load custom ModList settings when game type changes
|
||||
_modListSettings = this.WhenAny(x => x.SelectedGame)
|
||||
.Select(game =>
|
||||
{
|
||||
var gameSettings = _settings.ModlistSettings.TryCreate(game.Game);
|
||||
return new ModlistSettingsEditorVM(gameSettings.ModlistSettings);
|
||||
})
|
||||
// Interject and save old while loading new
|
||||
.Pairwise()
|
||||
.Do(pair =>
|
||||
{
|
||||
var (previous, current) = pair;
|
||||
previous?.Save();
|
||||
current?.Init();
|
||||
})
|
||||
.Select(x => x.Current)
|
||||
// Save to property
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(ModlistSettings));
|
||||
|
||||
// Wire start command
|
||||
BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.GameLocation.InError),
|
||||
this.WhenAny(x => x.DownloadsLocation.InError),
|
||||
this.WhenAny(x => x.StagingLocation.InError),
|
||||
(g, d, s) => !g && !d && !s)
|
||||
this.WhenAny(x => x.ModlistSettings)
|
||||
.Select(x => x?.InError ?? Observable.Return(false))
|
||||
.Switch(),
|
||||
(g, d, s, ml) => !g && !d && !s && !ml)
|
||||
.ObserveOnGuiThread(),
|
||||
execute: async () =>
|
||||
{
|
||||
@ -178,26 +201,6 @@ namespace Wabbajack
|
||||
})
|
||||
.DisposeWith(CompositeDisposable);
|
||||
|
||||
// Load custom ModList settings when game type changes
|
||||
_modListSettings = this.WhenAny(x => x.SelectedGame)
|
||||
.Select(game =>
|
||||
{
|
||||
var gameSettings = _settings.ModlistSettings.TryCreate(game.Game);
|
||||
return new ModlistSettingsEditorVM(gameSettings.ModlistSettings);
|
||||
})
|
||||
// Interject and save old while loading new
|
||||
.Pairwise()
|
||||
.Do(pair =>
|
||||
{
|
||||
var (previous, current) = pair;
|
||||
previous?.Save();
|
||||
current?.Init();
|
||||
})
|
||||
.Select(x => x.Current)
|
||||
// Save to property
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(ModlistSettings));
|
||||
|
||||
// Find game commands
|
||||
FindGameInSteamCommand = ReactiveCommand.Create(SetGameToSteamLocation);
|
||||
FindGameInGogCommand = ReactiveCommand.Create(SetGameToGogLocation);
|
||||
|
@ -109,7 +109,7 @@ namespace Wabbajack
|
||||
|
||||
ModListLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.On,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.On,
|
||||
PathType = FilePickerVM.PathTypeOptions.File,
|
||||
PromptTitle = "Select a modlist to install"
|
||||
};
|
||||
|
@ -41,7 +41,7 @@ namespace Wabbajack
|
||||
|
||||
Location = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.Off,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.Off,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select Installation Directory",
|
||||
};
|
||||
@ -49,7 +49,7 @@ namespace Wabbajack
|
||||
.Select(x => Utils.IsDirectoryPathValid(x));
|
||||
DownloadLocation = new FilePickerVM()
|
||||
{
|
||||
ExistCheckOption = FilePickerVM.ExistCheckOptions.Off,
|
||||
ExistCheckOption = FilePickerVM.CheckOptions.Off,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select a location for MO2 downloads",
|
||||
};
|
||||
|
@ -231,19 +231,16 @@
|
||||
<Compile Include="Views\Common\DetailImageView.xaml.cs">
|
||||
<DependentUpon>DetailImageView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Extensions\EnumerableExt.cs" />
|
||||
<Compile Include="Views\Common\TopProgressView.xaml.cs">
|
||||
<DependentUpon>TopProgressView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="View Models\FilePickerVM.cs" />
|
||||
<Compile Include="View Models\ModListVM.cs" />
|
||||
<Compile Include="View Models\ModVM.cs" />
|
||||
<Compile Include="Views\Compilers\CompilerView.xaml.cs">
|
||||
<DependentUpon>CompilerView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Converters\IsNotNullVisibilityConverter.cs" />
|
||||
<Compile Include="Extensions\ReactiveUIExt.cs" />
|
||||
<Compile Include="View Models\ModeSelectionVM.cs" />
|
||||
<Compile Include="Views\Common\FilePicker.xaml.cs">
|
||||
<DependentUpon>FilePicker.xaml</DependentUpon>
|
||||
|
Loading…
Reference in New Issue
Block a user