Merge pull request #385 from Noggog/download-crash

Modlist Download Crash
This commit is contained in:
Timothy Baldridge 2020-01-14 05:35:14 -08:00 committed by GitHub
commit ec0e7ca13c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 487 additions and 347 deletions

View File

@ -7,8 +7,8 @@ namespace Wabbajack
public readonly static ErrorResponse Success = Succeed();
public readonly static ErrorResponse Failure = new ErrorResponse();
public readonly bool Succeeded;
public readonly Exception Exception;
public bool Succeeded { get; }
public Exception Exception { get; }
private readonly string _reason;
public bool Failed => !Succeeded;

View File

@ -40,7 +40,7 @@ namespace Wabbajack
/// <param name="source">Source observable to subscribe to if on</param>
/// <param name="filterSwitch">On/Off signal of whether to subscribe to source observable</param>
/// <returns>Observable that publishes data from source, if the switch is on.</returns>
public static IObservable<T> FilterSwitch<T>(this IObservable<T> source, IObservable<bool> filterSwitch)
public static IObservable<T> FlowSwitch<T>(this IObservable<T> source, IObservable<bool> filterSwitch)
{
return filterSwitch
.DistinctUntilChanged()
@ -211,7 +211,7 @@ namespace Wabbajack
public static IObservable<T> DelayInitial<T>(this IObservable<T> source, TimeSpan delay)
{
return source.FilterSwitch(
return source.FlowSwitch(
Observable.Return(System.Reactive.Unit.Default)
.Delay(delay)
.Select(_ => true)
@ -220,7 +220,7 @@ namespace Wabbajack
public static IObservable<T> DelayInitial<T>(this IObservable<T> source, TimeSpan delay, IScheduler scheduler)
{
return source.FilterSwitch(
return source.FlowSwitch(
Observable.Return(System.Reactive.Unit.Default)
.Delay(delay, scheduler)
.Select(_ => true)

View File

@ -44,9 +44,6 @@ namespace Wabbajack.Lib.ModListRegistry
[JsonProperty("image")]
public string ImageUri { get; set; }
[JsonIgnore]
public Bitmap Image { get; set; }
[JsonProperty("readme")]
public string Readme { get; set; }

View File

@ -9,7 +9,6 @@ using DynamicData;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Common;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack.Test
{

View File

@ -22,13 +22,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.Test", "Wabbajack.Test\Wabbajack.Test.csproj", "{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.Lib", "Wabbajack.Lib\Wabbajack.Lib.csproj", "{0A820830-A298-497D-85E0-E9A89EFEF5FE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.Lib", "Wabbajack.Lib\Wabbajack.Lib.csproj", "{0A820830-A298-497D-85E0-E9A89EFEF5FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compression.BSA.Test", "Compression.BSA.Test\Compression.BSA.Test.csproj", "{9C004392-571A-4D28-A9F6-0E25115E6727}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.Common.CSP", "Wabbajack.Common.CSP\Wabbajack.Common.CSP.csproj", "{9E69BC98-1512-4977-B683-6E7E5292C0B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.VirtualFileSystem", "Wabbajack.VirtualFileSystem\Wabbajack.VirtualFileSystem.csproj", "{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.VirtualFileSystem", "Wabbajack.VirtualFileSystem\Wabbajack.VirtualFileSystem.csproj", "{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.VirtualFileSystem.Test", "Wabbajack.VirtualFileSystem.Test\Wabbajack.VirtualFileSystem.Test.csproj", "{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}"
EndProject
@ -63,10 +63,16 @@ Global
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Debug|x86.Build.0 = Debug|Any CPU
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|Any CPU.ActiveCfg = Release|x64
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|Any CPU.Build.0 = Release|x64
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|x86.ActiveCfg = Release|Any CPU
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|x86.Build.0 = Release|Any CPU
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|x64.ActiveCfg = Release|x64
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|x64.Build.0 = Release|x64
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|x86.ActiveCfg = Release|Any CPU
{B3F3FB6E-B9EB-4F49-9875-D78578BC7AE5}.Release|x86.Build.0 = Release|Any CPU
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug (no commandargs)|Any CPU
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug (no commandargs)|Any CPU.Build.0 = Debug (no commandargs)|Any CPU
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug (no commandargs)|x64.ActiveCfg = Debug (no commandargs)|x64
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug (no commandargs)|x64.Build.0 = Debug (no commandargs)|x64
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug (no commandargs)|x86.ActiveCfg = Debug (no commandargs)|x86
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug (no commandargs)|x86.Build.0 = Debug (no commandargs)|x86
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33602679-8484-40C7-A10C-774DFF5D8314}.Debug|x64.ActiveCfg = Debug|x64
@ -93,10 +99,10 @@ Global
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Debug|x86.Build.0 = Debug|Any CPU
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|Any CPU.ActiveCfg = Release|x64
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|Any CPU.Build.0 = Release|x64
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|x86.ActiveCfg = Release|Any CPU
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|x86.Build.0 = Release|Any CPU
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|x64.ActiveCfg = Release|x64
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|x64.Build.0 = Release|x64
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|x86.ActiveCfg = Release|Any CPU
{FF5D892F-8FF4-44FC-8F7F-CD58F307AD1B}.Release|x86.Build.0 = Release|Any CPU
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|x64
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Debug (no commandargs)|Any CPU.Build.0 = Debug|x64
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Debug (no commandargs)|x64.ActiveCfg = Debug|x64
@ -111,10 +117,10 @@ Global
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Debug|x86.Build.0 = Debug|x86
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|Any CPU.ActiveCfg = Release|x64
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|Any CPU.Build.0 = Release|x64
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|x86.ActiveCfg = Release|x86
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|x86.Build.0 = Release|x86
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|x64.ActiveCfg = Release|x64
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|x64.Build.0 = Release|x64
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|x86.ActiveCfg = Release|x86
{A47FFF32-782B-4D9F-8704-C98FB32FA8CC}.Release|x86.Build.0 = Release|x86
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|x64
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Debug (no commandargs)|Any CPU.Build.0 = Debug|x64
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Debug (no commandargs)|x64.ActiveCfg = Debug|x64
@ -129,10 +135,10 @@ Global
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Debug|x86.Build.0 = Debug|Any CPU
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|Any CPU.ActiveCfg = Release|x64
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|Any CPU.Build.0 = Release|x64
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|x86.ActiveCfg = Release|Any CPU
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|x86.Build.0 = Release|Any CPU
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|x64.ActiveCfg = Release|x64
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|x64.Build.0 = Release|x64
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|x86.ActiveCfg = Release|Any CPU
{0A820830-A298-497D-85E0-E9A89EFEF5FE}.Release|x86.Build.0 = Release|Any CPU
{9C004392-571A-4D28-A9F6-0E25115E6727}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|x64
{9C004392-571A-4D28-A9F6-0E25115E6727}.Debug (no commandargs)|Any CPU.Build.0 = Debug|x64
{9C004392-571A-4D28-A9F6-0E25115E6727}.Debug (no commandargs)|x64.ActiveCfg = Debug|Any CPU
@ -147,10 +153,10 @@ Global
{9C004392-571A-4D28-A9F6-0E25115E6727}.Debug|x86.Build.0 = Debug|Any CPU
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|Any CPU.ActiveCfg = Release|x64
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|Any CPU.Build.0 = Release|x64
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|x86.ActiveCfg = Release|Any CPU
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|x86.Build.0 = Release|Any CPU
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|x64.ActiveCfg = Release|x64
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|x64.Build.0 = Release|x64
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|x86.ActiveCfg = Release|Any CPU
{9C004392-571A-4D28-A9F6-0E25115E6727}.Release|x86.Build.0 = Release|Any CPU
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|x64
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Debug (no commandargs)|Any CPU.Build.0 = Debug|x64
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Debug (no commandargs)|x64.ActiveCfg = Debug|Any CPU
@ -165,10 +171,10 @@ Global
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Debug|x86.Build.0 = Debug|Any CPU
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|Any CPU.ActiveCfg = Release|x64
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|Any CPU.Build.0 = Release|x64
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|x86.ActiveCfg = Release|Any CPU
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|x86.Build.0 = Release|Any CPU
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|x64.ActiveCfg = Release|x64
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|x64.Build.0 = Release|x64
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|x86.ActiveCfg = Release|Any CPU
{9E69BC98-1512-4977-B683-6E7E5292C0B8}.Release|x86.Build.0 = Release|Any CPU
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Debug (no commandargs)|Any CPU.Build.0 = Debug|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Debug (no commandargs)|x64.ActiveCfg = Debug|Any CPU
@ -183,10 +189,10 @@ Global
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Debug|x86.Build.0 = Debug|Any CPU
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|Any CPU.ActiveCfg = Release|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|Any CPU.Build.0 = Release|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x86.ActiveCfg = Release|Any CPU
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x86.Build.0 = Release|Any CPU
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x64.ActiveCfg = Release|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x64.Build.0 = Release|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x86.ActiveCfg = Release|Any CPU
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x86.Build.0 = Release|Any CPU
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|x64
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Debug (no commandargs)|Any CPU.Build.0 = Debug|x64
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Debug (no commandargs)|x64.ActiveCfg = Debug|Any CPU
@ -201,10 +207,10 @@ Global
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Debug|x86.Build.0 = Debug|Any CPU
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|Any CPU.ActiveCfg = Release|x64
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|Any CPU.Build.0 = Release|x64
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|x86.ActiveCfg = Release|Any CPU
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|x86.Build.0 = Release|Any CPU
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|x64.ActiveCfg = Release|x64
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|x64.Build.0 = Release|x64
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|x86.ActiveCfg = Release|Any CPU
{51CEB604-985A-45B9-AF0D-C5BA8CFA1BF0}.Release|x86.Build.0 = Release|Any CPU
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|x64
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Debug (no commandargs)|Any CPU.Build.0 = Debug|x64
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Debug (no commandargs)|x64.ActiveCfg = Debug|Any CPU
@ -219,10 +225,10 @@ Global
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Debug|x86.Build.0 = Debug|Any CPU
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|Any CPU.ActiveCfg = Release|x64
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|Any CPU.Build.0 = Release|x64
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x86.ActiveCfg = Release|Any CPU
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x86.Build.0 = Release|Any CPU
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x64.ActiveCfg = Release|x64
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x64.Build.0 = Release|x64
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x86.ActiveCfg = Release|Any CPU
{37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x86.Build.0 = Release|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug (no commandargs)|Any CPU.ActiveCfg = Debug|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug (no commandargs)|Any CPU.Build.0 = Debug|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug (no commandargs)|x64.ActiveCfg = Debug|Any CPU
@ -237,10 +243,10 @@ Global
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug|x86.Build.0 = Debug|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|Any CPU.Build.0 = Release|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|x86.ActiveCfg = Release|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|x86.Build.0 = Release|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|x64.ActiveCfg = Release|x64
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|x64.Build.0 = Release|x64
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|x86.ActiveCfg = Release|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -56,10 +56,10 @@ namespace Wabbajack
public static IReactiveBinding<TView, TViewModel, (object view, bool isViewModel)> BindStrict<TViewModel, TView, TVMProp, TVProp>(
this TView view,
TViewModel viewModel,
TViewModel viewModel,
Expression<Func<TViewModel, TVMProp>> vmProperty,
Expression<Func<TView, TVProp>> viewProperty,
Func<TVMProp, TVProp> vmToViewConverter,
Expression<Func<TView, TVProp>> viewProperty,
Func<TVMProp, TVProp> vmToViewConverter,
Func<TVProp, TVMProp> viewToVmConverter)
where TViewModel : class
where TView : class, IViewFor
@ -71,5 +71,26 @@ namespace Wabbajack
vmToViewConverter: vmToViewConverter,
viewToVmConverter: viewToVmConverter);
}
public static IDisposable BindToStrict<TValue, TTarget>(
this IObservable<TValue> @this,
TTarget target,
Expression<Func<TTarget, TValue>> property)
where TTarget : class
{
return @this.BindTo<TValue, TTarget, TValue>(target, property);
}
/// <summary>
/// Just a function to signify a field is being used, so it triggers compile errors if it changes
/// </summary>
public static void MarkAsNeeded<TView, TViewModel, TVMProp>(
this TView view,
TViewModel viewModel,
Expression<Func<TViewModel, TVMProp>> vmProperty)
where TViewModel : class
where TView : class, IViewFor
{
}
}
}

View File

@ -1,4 +1,4 @@
using DynamicData;
using DynamicData;
using Microsoft.WindowsAPICodePack.Dialogs;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@ -10,7 +10,7 @@ using System.Reactive.Linq;
using System.Windows.Input;
using Wabbajack.Lib;
namespace Wabbajack.UI
namespace Wabbajack
{
public class FilePickerVM : ViewModel
{
@ -109,7 +109,7 @@ namespace Wabbajack.UI
_exists = Observable.Interval(TimeSpan.FromSeconds(3), RxApp.TaskpoolScheduler)
// Only check exists on timer if desired
.FilterSwitch(doExistsCheck)
.FlowSwitch(doExistsCheck)
.Unit()
// Also check though, when fields change
.Merge(this.WhenAny(x => x.PathType).Unit())

View File

@ -4,6 +4,7 @@ using Microsoft.WindowsAPICodePack.Dialogs;
using ReactiveUI;
using System;
using System.IO;
using System.Net.Http;
using System.Reactive.Linq;
using System.Reflection;
using System.Threading;
@ -12,7 +13,7 @@ using System.Windows.Forms;
using System.Windows.Media.Imaging;
using Wabbajack.Common;
namespace Wabbajack.UI
namespace Wabbajack
{
public static class UIUtils
{
@ -75,5 +76,49 @@ namespace Wabbajack.UI
.Bind(list)
.Subscribe();
}
public static IObservable<BitmapImage> DownloadBitmapImage(this IObservable<string> obs, Action<Exception> exceptionHandler)
{
return obs
.ObserveOn(RxApp.TaskpoolScheduler)
.SelectTask(async url =>
{
try
{
var ret = new MemoryStream();
using (var client = new HttpClient())
using (var stream = await client.GetStreamAsync(url))
{
stream.CopyTo(ret);
}
ret.Seek(0, SeekOrigin.Begin);
return ret;
}
catch (Exception ex)
{
exceptionHandler(ex);
return default;
}
})
.ObserveOnGuiThread()
.Select(memStream =>
{
if (memStream == null) return default;
try
{
return BitmapImageFromStream(memStream);
}
catch (Exception ex)
{
exceptionHandler(ex);
return default;
}
finally
{
memStream.Dispose();
}
});
}
}
}

View File

@ -25,12 +25,19 @@ namespace Wabbajack
public ViewModel NavigateBackTarget { get; set; }
public ReactiveCommand<Unit, Unit> BackCommand { get; protected set; }
private readonly ObservableAsPropertyHelper<bool> _IsActive;
public bool IsActive => _IsActive.Value;
public BackNavigatingVM(MainWindowVM mainWindowVM)
{
BackCommand = ReactiveCommand.Create(
execute: () => Utils.CatchAndLog(() => mainWindowVM.NavigateTo(NavigateBackTarget)),
canExecute: this.ConstructCanNavigateBack()
.ObserveOnGuiThread());
_IsActive = mainWindowVM.WhenAny(x => x.ActivePane)
.Select(x => object.ReferenceEquals(this, x))
.ToProperty(this, nameof(IsActive));
}
}

View File

@ -15,7 +15,6 @@ using System.Windows.Media.Imaging;
using Wabbajack.Common;
using Wabbajack.Common.StatusFeed;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack
{

View File

@ -9,7 +9,6 @@ using System.Reactive.Linq;
using System.Threading.Tasks;
using Wabbajack.Common;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack
{
@ -147,7 +146,7 @@ namespace Wabbajack
(this).WhenAny(x => x.Mo2Folder)
.DelayInitial(TimeSpan.FromMilliseconds(100))
.Where(x => Directory.Exists(x))
.FilterSwitch(
.FlowSwitch(
(this).WhenAny(x => x.DownloadLocation.Exists)
.Invert())
// A skip is needed to ignore the initial signal when the FilterSwitch turns on

View File

@ -6,7 +6,6 @@ using Microsoft.WindowsAPICodePack.Dialogs;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack
{

View File

@ -11,7 +11,6 @@ using ReactiveUI.Fody.Helpers;
using Wabbajack.Common;
using Wabbajack.Common.StoreHandlers;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack
{

View File

@ -21,7 +21,6 @@ using Wabbajack.Common.StatusFeed;
using System.Reactive;
using System.Collections.Generic;
using System.Windows.Input;
using Wabbajack.UI;
namespace Wabbajack
{

View File

@ -11,7 +11,6 @@ using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Wabbajack.Common;
using Wabbajack.Lib;
using Wabbajack.UI;
using Wabbajack.Util;
namespace Wabbajack

View File

@ -9,6 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using Alphaleonis.Win32.Filesystem;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@ -38,6 +39,12 @@ namespace Wabbajack
[Reactive]
public bool IsBroken { get; private set; }
[Reactive]
public IErrorResponse Error { get; private set; }
private readonly ObservableAsPropertyHelper<BitmapImage> _Image;
public BitmapImage Image => _Image.Value;
public ModListMetadataVM(ModListGalleryVM parent, ModlistMetadata metadata)
{
_parent = parent;
@ -57,7 +64,15 @@ namespace Wabbajack
{
if (!exists)
{
await Download();
try
{
await Download();
}
catch (Exception ex)
{
Error = ErrorResponse.Fail(ex);
return false;
}
// Return an updated check on exists
return File.Exists(Location);
}
@ -92,6 +107,7 @@ namespace Wabbajack
_Exists = Observable.Interval(TimeSpan.FromSeconds(0.5))
.Unit()
.StartWith(Unit.Default)
.FlowSwitch(_parent.WhenAny(x => x.IsActive))
.Select(_ =>
{
try
@ -104,6 +120,10 @@ namespace Wabbajack
}
})
.ToProperty(this, nameof(Exists));
_Image = Observable.Return(Metadata.Links.ImageUri)
.DownloadBitmapImage((ex) => Utils.Log($"Error downloading modlist image {Metadata.Title}"))
.ToProperty(this, nameof(Image));
}
private Task Download()

View File

@ -8,7 +8,6 @@ using System.Reactive.Linq;
using System.Windows.Media.Imaging;
using Wabbajack.Common;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack
{

View File

@ -8,7 +8,6 @@ using Wabbajack.Common;
using Wabbajack.Lib;
using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.NexusApi;
using Wabbajack.UI;
namespace Wabbajack
{
@ -44,44 +43,7 @@ namespace Wabbajack
ImageURL = m.SlideShowPic;
ImageObservable = Observable.Return(ImageURL)
.ObserveOn(RxApp.TaskpoolScheduler)
.SelectTask(async url =>
{
try
{
var ret = new MemoryStream();
using (var client = new HttpClient())
using (var stream = await client.GetStreamAsync(url))
{
stream.CopyTo(ret);
}
ret.Seek(0, SeekOrigin.Begin);
return ret;
}
catch (Exception)
{
Utils.Log($"Skipping slide for mod {ModName} ({ModID})");
return default;
}
})
.ObserveOnGuiThread()
.Select(memStream =>
{
if (memStream == null) return default;
try
{
return UIUtils.BitmapImageFromStream(memStream);
}
catch (Exception)
{
Utils.Log($"Skipping slide for mod {ModName} ({ModID})");
return default;
}
finally
{
memStream.Dispose();
}
})
.DownloadBitmapImage((ex) => Utils.Log($"Skipping slide for mod {ModName} ({ModID})"))
.Replay(1)
.RefCount(TimeSpan.FromMilliseconds(5000));
}

View File

@ -6,7 +6,6 @@ using System.Reactive.Linq;
using System.Windows.Input;
using Wabbajack.Common;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack
{

View File

@ -60,7 +60,7 @@ namespace Wabbajack
// When filter switch enabled, fire an initial signal
.StartWith(Unit.Default)
// Only subscribe to slideshow triggers if enabled and installing
.FilterSwitch(
.FlowSwitch(
Observable.CombineLatest(
this.WhenAny(x => x.Enable),
this.WhenAny(x => x.Installer.Installing),

View File

@ -2,8 +2,6 @@
using System.Windows.Controls;
using System.Windows.Data;
using Wabbajack.Lib;
using Wabbajack.UI;
namespace Wabbajack
{
/// <summary>

View File

@ -5,46 +5,11 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:local="clr-namespace:Wabbajack"
xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<Color x:Key="TextBackgroundFill">#92000000</Color>
<SolidColorBrush x:Key="TextBackgroundFillBrush" Color="{StaticResource TextBackgroundFill}" />
<Color x:Key="TextBackgroundHoverFill">#DF000000</Color>
<Style x:Key="BackgroundBlurStyle" TargetType="TextBlock">
<Setter Property="Background" Value="{StaticResource TextBackgroundFillBrush}" />
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)"
To="{StaticResource TextBackgroundHoverFill}"
Duration="0:0:0.06" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)"
To="{StaticResource TextBackgroundFill}"
Duration="0:0:0.06" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="47" />
@ -77,228 +42,7 @@
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
x:Name="ModListTile"
Margin="10"
Background="Transparent"
BorderThickness="1">
<Border.Effect>
<DropShadowEffect
BlurRadius="25"
Opacity="0.5"
ShadowDepth="5" />
</Border.Effect>
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="{StaticResource ButtonBorder}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=ModListTile}" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource BorderInterestBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid
Width="570"
Height="440"
Background="{StaticResource DarkBackgroundBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
BorderBrush="{StaticResource ButtonNormalBorder}"
BorderThickness="0,0,0,1">
<Grid ClipToBounds="True">
<Viewbox
Height="340"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="UniformToFill">
<Image Source="{Binding Metadata.Links.ImageUri}">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBroken}" Value="True">
<Setter Property="Effect">
<Setter.Value>
<BlurEffect Radius="35" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Viewbox>
<Ellipse
Height="120"
Margin="-40,0,-40,-60"
VerticalAlignment="Bottom"
Fill="Black"
Opacity="0.5">
<Ellipse.Effect>
<BlurEffect Radius="55" />
</Ellipse.Effect>
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="0.75"
Duration="0:0:0.08" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:0.08" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</Grid>
</Border>
<local:UnderMaintenanceOverlay
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Visibility="{Binding IsBroken, Converter={StaticResource bool2VisibilityConverter}}" />
<TextBlock
x:Name="DescriptionTextShadow"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontFamily="Lucida Sans"
FontSize="30"
FontWeight="Bold"
Style="{StaticResource BackgroundBlurStyle}"
Text="{Binding Metadata.Title}"
TextWrapping="Wrap">
<TextBlock.Effect>
<BlurEffect Radius="25" />
</TextBlock.Effect>
</TextBlock>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontFamily="Lucida Sans"
FontSize="30"
FontWeight="Bold"
Text="{Binding Metadata.Title}"
TextWrapping="Wrap">
<TextBlock.Effect>
<DropShadowEffect />
</TextBlock.Effect>
</TextBlock>
<mahapps:MetroProgressBar
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Height="3"
VerticalAlignment="Bottom"
Background="{StaticResource BackgroundBrush}"
Foreground="{StaticResource SecondaryBrush}"
Maximum="1"
Visibility="{Binding IsEnabled, ElementName=ExecuteButton, Converter={StaticResource bool2VisibilityHiddenConverter}, ConverterParameter=False}"
Value="{Binding ProgressPercent, Mode=OneWay}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="8,5"
VerticalAlignment="Center"
FontSize="14"
Text="{Binding Metadata.Description}"
TextWrapping="Wrap" />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Width="40"
Height="40"
Margin="5,0"
VerticalAlignment="Bottom"
Command="{Binding OpenWebsiteCommand}"
Style="{StaticResource IconBareButtonStyle}">
<iconPacks:Material
Width="20"
Height="20"
Kind="Web" />
</Button>
<Button
x:Name="ExecuteButton"
Grid.Row="1"
Width="40"
Height="40"
Margin="5,0"
VerticalAlignment="Top"
Command="{Binding ExecuteCommand}">
<Button.Style>
<Style BasedOn="{StaticResource IconBareButtonStyle}" TargetType="Button">
<Setter Property="Content">
<Setter.Value>
<iconPacks:Material
Width="20"
Height="20"
Kind="Download" />
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Download modlist" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Exists}" Value="True" />
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Content">
<Setter.Value>
<iconPacks:Material
Width="20"
Height="20"
Kind="Play" />
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Install modlist" />
<Setter Property="Padding" Value="3,0,0,0" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Grid>
</Border>
<local:ModListTileView ViewModel="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

View File

@ -0,0 +1,282 @@
<rxui:ReactiveUserControl
x:Class="Wabbajack.ModListTileView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:local="clr-namespace:Wabbajack"
xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rxui="http://reactiveui.net"
d:DesignHeight="450"
d:DesignWidth="800"
x:TypeArguments="local:ModListMetadataVM"
mc:Ignorable="d">
<UserControl.Resources>
<Color x:Key="TextBackgroundFill">#92000000</Color>
<SolidColorBrush x:Key="TextBackgroundFillBrush" Color="{StaticResource TextBackgroundFill}" />
<Color x:Key="TextBackgroundHoverFill">#DF000000</Color>
<Style x:Key="BackgroundBlurStyle" TargetType="TextBlock">
<Setter Property="Background" Value="{StaticResource TextBackgroundFillBrush}" />
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)"
To="{StaticResource TextBackgroundHoverFill}"
Duration="0:0:0.06" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)"
To="{StaticResource TextBackgroundFill}"
Duration="0:0:0.06" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Border
x:Name="ModListTile"
Margin="10"
Background="Transparent"
BorderThickness="1">
<Border.Effect>
<DropShadowEffect
BlurRadius="25"
Opacity="0.5"
ShadowDepth="5" />
</Border.Effect>
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="{StaticResource ButtonBorder}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=ModListTile}" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource BorderInterestBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid
Width="570"
Height="440"
Background="{StaticResource DarkBackgroundBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
BorderBrush="{StaticResource ButtonNormalBorder}"
BorderThickness="0,0,0,1">
<Grid ClipToBounds="True">
<Viewbox
Height="340"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="UniformToFill">
<Image x:Name="ModListImage" Source="{Binding Metadata.Links.ImageUri}">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBroken}" Value="True">
<Setter Property="Effect">
<Setter.Value>
<BlurEffect Radius="35" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Viewbox>
<Ellipse
Height="120"
Margin="-40,0,-40,-60"
VerticalAlignment="Bottom"
Fill="Black"
Opacity="0.5">
<Ellipse.Effect>
<BlurEffect Radius="55" />
</Ellipse.Effect>
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="0.75"
Duration="0:0:0.08" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:0.08" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</Grid>
</Border>
<local:UnderMaintenanceOverlay
x:Name="Overlay"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2" />
<TextBlock
x:Name="DescriptionTextShadow"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontFamily="Lucida Sans"
FontSize="30"
FontWeight="Bold"
Style="{StaticResource BackgroundBlurStyle}"
TextWrapping="Wrap">
<TextBlock.Effect>
<BlurEffect Radius="25" />
</TextBlock.Effect>
</TextBlock>
<TextBlock
x:Name="ModListTitleShadow"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontFamily="Lucida Sans"
FontSize="30"
FontWeight="Bold"
TextWrapping="Wrap">
<TextBlock.Effect>
<DropShadowEffect />
</TextBlock.Effect>
</TextBlock>
<mahapps:MetroProgressBar
x:Name="DownloadProgressBar"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Height="3"
VerticalAlignment="Bottom"
Background="{StaticResource BackgroundBrush}"
Foreground="{StaticResource SecondaryBrush}"
Maximum="1"
Visibility="{Binding IsEnabled, ElementName=ExecuteButton, Converter={StaticResource bool2VisibilityHiddenConverter}, ConverterParameter=False}"
Value="{Binding ProgressPercent, Mode=OneWay}" />
<TextBlock
x:Name="MetadataDescription"
Grid.Row="1"
Grid.Column="0"
Margin="8,5"
VerticalAlignment="Center"
FontSize="14"
TextWrapping="Wrap" />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
x:Name="OpenWebsiteButton"
Grid.Row="0"
Width="40"
Height="40"
Margin="5,0"
VerticalAlignment="Bottom"
Style="{StaticResource IconBareButtonStyle}">
<iconPacks:Material
Width="20"
Height="20"
Kind="Web" />
</Button>
<Button
x:Name="ExecuteButton"
Grid.Row="1"
Width="40"
Height="40"
Margin="5,0"
VerticalAlignment="Top">
<Button.Style>
<Style BasedOn="{StaticResource IconBareButtonStyle}" TargetType="Button">
<Setter Property="Content">
<Setter.Value>
<iconPacks:Material
Width="20"
Height="20"
Kind="Download" />
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Download modlist" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Exists}" Value="True" />
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Content">
<Setter.Value>
<iconPacks:Material
Width="20"
Height="20"
Kind="Play" />
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Install modlist" />
<Setter Property="Padding" Value="3,0,0,0" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<DataTrigger Binding="{Binding Error.Succeeded}" Value="False">
<Setter Property="Content">
<Setter.Value>
<iconPacks:Material
Width="20"
Height="20"
Kind="AlertCircle" />
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Error downloading modlist. Check logs and/or restart Wabbajack to try again." />
<Setter Property="Foreground" Value="{StaticResource ErrorBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Grid>
</Border>
</rxui:ReactiveUserControl>

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ReactiveUI;
namespace Wabbajack
{
/// <summary>
/// Interaction logic for ModListTileView.xaml
/// </summary>
public partial class ModListTileView : ReactiveUserControl<ModListMetadataVM>
{
public ModListTileView()
{
InitializeComponent();
this.WhenActivated(dispose =>
{
this.MarkAsNeeded<ModListTileView, ModListMetadataVM, double>(this.ViewModel, x => x.ProgressPercent);
this.MarkAsNeeded<ModListTileView, ModListMetadataVM, bool>(this.ViewModel, x => x.IsBroken);
this.MarkAsNeeded<ModListTileView, ModListMetadataVM, bool>(this.ViewModel, x => x.Exists);
this.MarkAsNeeded<ModListTileView, ModListMetadataVM, string>(this.ViewModel, x => x.Metadata.Links.ImageUri);
this.WhenAny(x => x.ViewModel.Metadata.Title)
.BindToStrict(this, x => x.DescriptionTextShadow.Text)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.Metadata.Title)
.BindToStrict(this, x => x.ModListTitleShadow.Text)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.IsBroken)
.Select(x => x ? Visibility.Visible : Visibility.Collapsed)
.BindToStrict(this, x => x.Overlay.Visibility)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.Metadata.Description)
.BindToStrict(this, x => x.MetadataDescription.Text)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.OpenWebsiteCommand)
.BindToStrict(this, x => x.OpenWebsiteButton.Command)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.ExecuteCommand)
.BindToStrict(this, x => x.ExecuteButton.Command)
.DisposeWith(dispose);
this.WhenAny(x => x.ViewModel.Image)
.BindToStrict(this, x => x.ModListImage.Source)
.DisposeWith(dispose);
});
}
}
}

View File

@ -173,6 +173,9 @@
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Extensions\DynamicDataExt.cs" />
<Compile Include="Views\ModListTileView.xaml.cs">
<DependentUpon>ModListTileView.xaml</DependentUpon>
</Compile>
<Compile Include="UI\FilePickerVM.cs" />
<Compile Include="UI\UIUtils.cs" />
<Compile Include="Util\SystemParametersConstructor.cs" />
@ -301,6 +304,10 @@
<Compile Include="Views\WebBrowserView.xaml.cs">
<DependentUpon>WebBrowserView.xaml</DependentUpon>
</Compile>
<Page Include="Views\ModListTileView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Settings\LoginItemView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>