2019-12-03 05:35:51 +00:00
|
|
|
|
using DynamicData;
|
2019-10-28 04:36:46 +00:00
|
|
|
|
using ReactiveUI;
|
2019-11-02 23:23:11 +00:00
|
|
|
|
using ReactiveUI.Fody.Helpers;
|
2019-10-13 19:53:49 +00:00
|
|
|
|
using System;
|
2019-10-14 01:15:41 +00:00
|
|
|
|
using System.Diagnostics;
|
2019-10-11 10:14:01 +00:00
|
|
|
|
using System.Linq;
|
2019-10-13 19:53:49 +00:00
|
|
|
|
using System.Reactive;
|
2019-12-21 20:22:41 +00:00
|
|
|
|
using System.Reactive.Disposables;
|
2019-10-13 19:53:49 +00:00
|
|
|
|
using System.Reactive.Linq;
|
2020-03-04 12:10:49 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2019-10-11 10:14:01 +00:00
|
|
|
|
using System.Windows.Media.Imaging;
|
2019-12-21 20:22:41 +00:00
|
|
|
|
using Wabbajack.Common;
|
2019-10-16 03:10:34 +00:00
|
|
|
|
using Wabbajack.Lib;
|
2019-10-28 04:47:17 +00:00
|
|
|
|
using Wabbajack.Lib.Downloaders;
|
2019-10-11 10:14:01 +00:00
|
|
|
|
|
2019-10-13 20:24:33 +00:00
|
|
|
|
namespace Wabbajack
|
2019-10-11 10:14:01 +00:00
|
|
|
|
{
|
2019-10-13 19:12:33 +00:00
|
|
|
|
public class SlideShow : ViewModel
|
2019-10-11 10:14:01 +00:00
|
|
|
|
{
|
2019-11-03 06:01:19 +00:00
|
|
|
|
private readonly Random _random = new Random();
|
2019-10-13 19:12:33 +00:00
|
|
|
|
|
2019-10-25 04:26:29 +00:00
|
|
|
|
public InstallerVM Installer { get; }
|
2019-10-13 19:12:33 +00:00
|
|
|
|
|
2019-11-02 23:23:11 +00:00
|
|
|
|
[Reactive]
|
|
|
|
|
public bool ShowNSFW { get; set; }
|
2019-10-13 19:12:33 +00:00
|
|
|
|
|
2019-11-02 23:23:11 +00:00
|
|
|
|
[Reactive]
|
|
|
|
|
public bool Enable { get; set; } = true;
|
2019-10-13 19:53:49 +00:00
|
|
|
|
|
2019-11-21 15:45:00 +00:00
|
|
|
|
private readonly ObservableAsPropertyHelper<BitmapImage> _image;
|
|
|
|
|
public BitmapImage Image => _image.Value;
|
2019-10-13 19:53:49 +00:00
|
|
|
|
|
2019-11-21 15:45:00 +00:00
|
|
|
|
private readonly ObservableAsPropertyHelper<ModVM> _targetMod;
|
|
|
|
|
public ModVM TargetMod => _targetMod.Value;
|
2019-10-14 01:15:41 +00:00
|
|
|
|
|
2020-01-21 01:07:41 +00:00
|
|
|
|
public ReactiveCommand<Unit, Unit> SlideShowNextItemCommand { get; } = ReactiveCommand.Create(() => { });
|
2020-03-04 12:10:49 +00:00
|
|
|
|
public ReactiveCommand<Unit, Unit> VisitURLCommand { get; }
|
2019-10-13 19:53:49 +00:00
|
|
|
|
|
2019-12-21 20:22:41 +00:00
|
|
|
|
public const int PreloadAmount = 4;
|
|
|
|
|
|
2019-10-25 04:26:29 +00:00
|
|
|
|
public SlideShow(InstallerVM appState)
|
2019-10-11 10:14:01 +00:00
|
|
|
|
{
|
2019-11-21 15:04:33 +00:00
|
|
|
|
Installer = appState;
|
2019-10-13 19:53:49 +00:00
|
|
|
|
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// Wire target slideshow index
|
|
|
|
|
var intervalSeconds = 10;
|
|
|
|
|
// Compile all the sources that trigger a slideshow update, any of which trigger a counter update
|
|
|
|
|
var selectedIndex = Observable.Merge(
|
2019-10-13 19:53:49 +00:00
|
|
|
|
// If user requests one manually
|
2019-11-21 15:04:33 +00:00
|
|
|
|
SlideShowNextItemCommand.StartingExecution(),
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// If the natural timer fires
|
|
|
|
|
Observable.Merge(
|
|
|
|
|
// Start with an initial timer
|
|
|
|
|
Observable.Return(Observable.Interval(TimeSpan.FromSeconds(intervalSeconds))),
|
|
|
|
|
// but reset timer if user requests one
|
2019-11-21 15:04:33 +00:00
|
|
|
|
SlideShowNextItemCommand.StartingExecution()
|
2019-11-03 06:01:19 +00:00
|
|
|
|
.Select(_ => Observable.Interval(TimeSpan.FromSeconds(intervalSeconds))))
|
|
|
|
|
// When a new timer comes in, swap to it
|
|
|
|
|
.Switch()
|
2020-02-04 00:45:47 +00:00
|
|
|
|
.Unit()
|
|
|
|
|
// Only subscribe to timer if enabled and installing
|
|
|
|
|
.FlowSwitch(
|
|
|
|
|
Observable.CombineLatest(
|
|
|
|
|
this.WhenAny(x => x.Enable),
|
|
|
|
|
this.WhenAny(x => x.Installer.Installing),
|
|
|
|
|
resultSelector: (enabled, installing) => enabled && installing)))
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// When filter switch enabled, fire an initial signal
|
2019-10-13 19:53:49 +00:00
|
|
|
|
.StartWith(Unit.Default)
|
2020-02-04 00:45:47 +00:00
|
|
|
|
// Only subscribe to slideshow triggers if started
|
|
|
|
|
.FlowSwitch(this.WhenAny(x => x.Installer.StartedInstallation))
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// Block spam
|
2020-01-17 04:52:29 +00:00
|
|
|
|
.Debounce(TimeSpan.FromMilliseconds(250), RxApp.MainThreadScheduler)
|
2019-11-03 06:01:19 +00:00
|
|
|
|
.Scan(
|
|
|
|
|
seed: 0,
|
|
|
|
|
accumulator: (i, _) => i + 1)
|
|
|
|
|
.Publish()
|
|
|
|
|
.RefCount();
|
|
|
|
|
|
|
|
|
|
// Dynamic list changeset of mod VMs to display
|
|
|
|
|
var modVMs = this.WhenAny(x => x.Installer.ModList)
|
|
|
|
|
// Whenever modlist changes, grab the list of its slides
|
|
|
|
|
.Select(modList =>
|
2019-10-11 10:14:01 +00:00
|
|
|
|
{
|
2019-12-03 05:35:51 +00:00
|
|
|
|
if (modList?.SourceModList?.Archives == null)
|
2019-10-12 19:15:19 +00:00
|
|
|
|
{
|
2020-03-04 12:10:49 +00:00
|
|
|
|
return Observable.Empty<IMetaState>()
|
|
|
|
|
.ToObservableChangeSet(x => x.URL);
|
2019-10-12 19:15:19 +00:00
|
|
|
|
}
|
2019-11-03 06:01:19 +00:00
|
|
|
|
return modList.SourceModList.Archives
|
|
|
|
|
.Select(m => m.State)
|
2020-03-04 12:10:49 +00:00
|
|
|
|
.OfType<IMetaState>()
|
|
|
|
|
.DistinctBy(x => x.URL)
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// Shuffle it
|
2019-11-21 15:04:33 +00:00
|
|
|
|
.Shuffle(_random)
|
2020-03-04 12:10:49 +00:00
|
|
|
|
.AsObservableChangeSet(x => x.URL);
|
2019-11-03 06:01:19 +00:00
|
|
|
|
})
|
2020-01-13 21:11:07 +00:00
|
|
|
|
// Switch to the new list after every ModList change
|
2019-11-03 06:01:19 +00:00
|
|
|
|
.Switch()
|
2020-03-04 12:10:49 +00:00
|
|
|
|
.Transform(mod => new ModVM(mod))
|
2020-01-16 03:54:06 +00:00
|
|
|
|
.DisposeMany()
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// Filter out any NSFW slides if we don't want them
|
|
|
|
|
.AutoRefreshOnObservable(slide => this.WhenAny(x => x.ShowNSFW))
|
2020-03-04 12:10:49 +00:00
|
|
|
|
.Filter(slide => !slide.State.IsNSFW || ShowNSFW)
|
2019-11-03 06:01:19 +00:00
|
|
|
|
.RefCount();
|
|
|
|
|
|
|
|
|
|
// Find target mod to display by combining dynamic list with currently desired index
|
2019-11-21 15:45:00 +00:00
|
|
|
|
_targetMod = Observable.CombineLatest(
|
2019-11-03 06:01:19 +00:00
|
|
|
|
modVMs.QueryWhenChanged(),
|
|
|
|
|
selectedIndex,
|
2019-12-21 20:22:41 +00:00
|
|
|
|
resultSelector: (query, selected) =>
|
|
|
|
|
{
|
|
|
|
|
var index = selected % (query.Count == 0 ? 1 : query.Count);
|
|
|
|
|
return query.Items.ElementAtOrDefault(index);
|
|
|
|
|
})
|
2019-11-03 06:01:19 +00:00
|
|
|
|
.StartWith(default(ModVM))
|
2020-01-17 04:48:54 +00:00
|
|
|
|
.ToGuiProperty(this, nameof(TargetMod));
|
2019-10-11 10:14:01 +00:00
|
|
|
|
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// Mark interest and materialize image of target mod
|
2019-11-21 15:45:00 +00:00
|
|
|
|
_image = this.WhenAny(x => x.TargetMod)
|
2019-11-03 06:01:19 +00:00
|
|
|
|
// We want to Switch here, not SelectMany, as we want to hotswap to newest target without waiting on old ones
|
|
|
|
|
.Select(x => x?.ImageObservable ?? Observable.Return(default(BitmapImage)))
|
|
|
|
|
.Switch()
|
2020-01-17 04:48:54 +00:00
|
|
|
|
.ToGuiProperty(this, nameof(Image));
|
2019-10-11 10:14:01 +00:00
|
|
|
|
|
2020-03-04 12:10:49 +00:00
|
|
|
|
VisitURLCommand = ReactiveCommand.Create(
|
2020-01-21 01:07:41 +00:00
|
|
|
|
execute: () =>
|
|
|
|
|
{
|
2020-03-04 12:10:49 +00:00
|
|
|
|
Utils.OpenWebsite(TargetMod.State.URL);
|
2020-01-21 01:07:41 +00:00
|
|
|
|
return Unit.Default;
|
|
|
|
|
},
|
2020-03-04 12:10:49 +00:00
|
|
|
|
canExecute: this.WhenAny(x => x.TargetMod.State.URL)
|
|
|
|
|
.Select(x =>
|
|
|
|
|
{
|
|
|
|
|
var regex = new Regex("^(http|https):\\/\\/");
|
|
|
|
|
return x != null && regex.Match(x).Success;
|
|
|
|
|
})
|
2019-11-03 06:01:19 +00:00
|
|
|
|
.ObserveOnGuiThread());
|
2019-10-11 10:14:01 +00:00
|
|
|
|
|
2019-12-21 20:22:41 +00:00
|
|
|
|
// Preload upcoming images
|
|
|
|
|
var list = Observable.CombineLatest(
|
|
|
|
|
modVMs.QueryWhenChanged(),
|
|
|
|
|
selectedIndex,
|
|
|
|
|
resultSelector: (query, selected) =>
|
|
|
|
|
{
|
|
|
|
|
// Retrieve the mods that should be preloaded
|
|
|
|
|
var index = selected % (query.Count == 0 ? 1 : query.Count);
|
|
|
|
|
var amountToTake = Math.Min(query.Count - index, PreloadAmount);
|
|
|
|
|
return query.Items.Skip(index).Take(amountToTake).ToObservable();
|
|
|
|
|
})
|
|
|
|
|
.Select(i => i.ToObservableChangeSet())
|
|
|
|
|
.Switch()
|
|
|
|
|
.Transform(mod => mod.ImageObservable.Subscribe())
|
|
|
|
|
.DisposeMany()
|
|
|
|
|
.AsObservableList();
|
2019-10-11 10:14:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-21 15:04:33 +00:00
|
|
|
|
}
|