public SolutionPatcherVM(ProfileVM parent, SolutionPatcherSettings?settings = null) : base(parent, settings) { CopyInSettings(settings); SolutionPath.Filters.Add(new CommonFileDialogFilter("Solution", ".sln")); SelectedProjectPath.Filters.Add(new CommonFileDialogFilter("Project", ".csproj")); _DisplayName = Observable.CombineLatest( this.WhenAnyValue(x => x.Nickname), this.WhenAnyValue(x => x.SelectedProjectPath.TargetPath) .StartWith(settings?.ProjectSubpath ?? string.Empty), (nickname, path) => { if (!string.IsNullOrWhiteSpace(nickname)) { return(nickname); } try { var name = Path.GetFileName(Path.GetDirectoryName(path)); if (string.IsNullOrWhiteSpace(name)) { return(string.Empty); } return(name); } catch (Exception) { return(string.Empty); } }) .ToProperty(this, nameof(DisplayName), Nickname); AvailableProjects = SolutionPatcherConfigLogic.AvailableProject( this.WhenAnyValue(x => x.SolutionPath.TargetPath)) .ObserveOnGui() .ToObservableCollection(this); var projPath = SolutionPatcherConfigLogic.ProjectPath( solutionPath: this.WhenAnyValue(x => x.SolutionPath.TargetPath), projectSubpath: this.WhenAnyValue(x => x.ProjectSubpath)); projPath .Subscribe(p => SelectedProjectPath.TargetPath = p) .DisposeWith(this); _State = Observable.CombineLatest( this.WhenAnyValue(x => x.SolutionPath.ErrorState), this.WhenAnyValue(x => x.SelectedProjectPath.ErrorState), this.WhenAnyValue(x => x.Profile.Config.MainVM) .Select(x => x.DotNetSdkInstalled) .Switch(), (sln, proj, dotnet) => { if (sln.Failed) { return(new ConfigurationState(sln)); } if (!dotnet.Acceptable) { return(new ConfigurationState(ErrorResponse.Fail("No dotnet SDK installed"))); } return(new ConfigurationState(proj)); }) .ToGuiProperty <ConfigurationState>(this, nameof(State), new ConfigurationState(ErrorResponse.Fail("Evaluating")) { IsHaltingError = false }); OpenSolutionCommand = ReactiveCommand.Create( canExecute: this.WhenAnyValue(x => x.SolutionPath.InError) .Select(x => !x), execute: () => { try { Process.Start(new ProcessStartInfo(SolutionPath.TargetPath) { UseShellExecute = true, }); } catch (Exception ex) { Log.Logger.Error(ex, $"Error opening solution: {SolutionPath.TargetPath}"); } }); var metaPath = this.WhenAnyValue(x => x.SelectedProjectPath.TargetPath) .Select(projPath => { try { return(Path.Combine(Path.GetDirectoryName(projPath) !, Constants.MetaFileName)); } catch (Exception) { return(string.Empty); } }) .Replay(1) .RefCount(); // Set up meta file sync metaPath .Select(path => { return(Noggog.ObservableExt.WatchFile(path) .StartWith(Unit.Default) .Throttle(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler) .Select(_ => { if (!File.Exists(path)) { return default; } try { return JsonConvert.DeserializeObject <PatcherCustomization>( File.ReadAllText(path), Execution.Constants.JsonSettings); } catch (Exception ex) { Logger.Error(ex, "Error reading in meta"); } return default(PatcherCustomization?); })); }) .Switch() .DistinctUntilChanged() .ObserveOnGui() .Subscribe(info => { if (info == null) { return; } if (info.Nickname != null) { this.Nickname = info.Nickname; } this.LongDescription = info.LongDescription ?? string.Empty; this.ShortDescription = info.OneLineDescription ?? string.Empty; this.Visibility = info.Visibility; this.Versioning = info.PreferredAutoVersioning; this.RequiredMods.SetTo(info.RequiredMods .SelectWhere(x => TryGet <ModKey> .Create(ModKey.TryFromNameAndExtension(x, out var modKey), modKey)) .Select(m => new ModKeyItemViewModel(m))); }) .DisposeWith(this); Observable.CombineLatest( this.WhenAnyValue(x => x.DisplayName), this.WhenAnyValue(x => x.ShortDescription), this.WhenAnyValue(x => x.LongDescription), this.WhenAnyValue(x => x.Visibility), this.WhenAnyValue(x => x.Versioning), this.RequiredMods .ToObservableChangeSet() .Transform(x => x.ModKey) .AddKey(x => x) .Sort(ModKey.Alphabetical, SortOptimisations.ComparesImmutableValuesOnly, resetThreshold: 0) .QueryWhenChanged() .Select(x => x.Items) .StartWith(Enumerable.Empty <ModKey>()), metaPath, (nickname, shortDesc, desc, visibility, versioning, reqMods, meta) => (nickname, shortDesc, desc, visibility, versioning, reqMods: reqMods.Select(x => x.FileName).OrderBy(x => x).ToArray(), meta)) .DistinctUntilChanged() .Throttle(TimeSpan.FromMilliseconds(200), RxApp.MainThreadScheduler) .Skip(1) .Subscribe(x => { try { if (string.IsNullOrWhiteSpace(x.meta)) { return; } File.WriteAllText(x.meta, JsonConvert.SerializeObject( new PatcherCustomization() { OneLineDescription = x.shortDesc, LongDescription = x.desc, Visibility = x.visibility, Nickname = x.nickname, PreferredAutoVersioning = x.versioning, RequiredMods = x.reqMods }, Formatting.Indented, Execution.Constants.JsonSettings)); } catch (Exception ex) { Logger.Error(ex, "Error writing out meta"); } }) .DisposeWith(this); ReloadAutogeneratedSettingsCommand = ReactiveCommand.Create(() => { }); PatcherSettings = new PatcherSettingsVM( Logger, this, projPath .Merge(ReloadAutogeneratedSettingsCommand.EndingExecution() .WithLatestFrom(projPath, (_, p) => p)) .Select(p => (GetResponse <string> .Succeed(p), default(string?))), needBuild: true) .DisposeWith(this); }
public SolutionPatcherVM(ProfileVM parent, SolutionPatcherSettings?settings = null) : base(parent, settings) { CopyInSettings(settings); SolutionPath.Filters.Add(new CommonFileDialogFilter("Solution", ".sln")); SelectedProjectPath.Filters.Add(new CommonFileDialogFilter("Project", ".csproj")); _DisplayName = Observable.CombineLatest( this.WhenAnyValue(x => x.Nickname), this.WhenAnyValue(x => x.SelectedProjectPath.TargetPath) .StartWith(settings?.ProjectSubpath ?? string.Empty), (nickname, path) => { if (!string.IsNullOrWhiteSpace(nickname)) { return(nickname); } try { var name = Path.GetFileName(Path.GetDirectoryName(path)); if (string.IsNullOrWhiteSpace(name)) { return(string.Empty); } return(name); } catch (Exception) { return(string.Empty); } }) .ToProperty(this, nameof(DisplayName), Nickname); AvailableProjects = SolutionPatcherConfigLogic.AvailableProject( this.WhenAnyValue(x => x.SolutionPath.TargetPath)) .ObserveOnGui() .ToObservableCollection(this); var projPath = SolutionPatcherConfigLogic.ProjectPath( solutionPath: this.WhenAnyValue(x => x.SolutionPath.TargetPath), projectSubpath: this.WhenAnyValue(x => x.ProjectSubpath)); projPath .Subscribe(p => SelectedProjectPath.TargetPath = p) .DisposeWith(this); _State = Observable.CombineLatest( this.WhenAnyValue(x => x.SolutionPath.ErrorState), this.WhenAnyValue(x => x.SelectedProjectPath.ErrorState), this.WhenAnyValue(x => x.Profile.Config.MainVM) .Select(x => x.DotNetSdkInstalled) .Switch(), (sln, proj, dotnet) => { if (sln.Failed) { return(new ConfigurationState(sln)); } if (dotnet == null) { return(new ConfigurationState(ErrorResponse.Fail("No dotnet SDK installed"))); } return(new ConfigurationState(proj)); }) .ToGuiProperty <ConfigurationState>(this, nameof(State), new ConfigurationState(ErrorResponse.Fail("Evaluating")) { IsHaltingError = false }); OpenSolutionCommand = ReactiveCommand.Create( canExecute: this.WhenAnyValue(x => x.SolutionPath.InError) .Select(x => !x), execute: () => { try { Process.Start(new ProcessStartInfo(SolutionPath.TargetPath) { UseShellExecute = true, }); } catch (Exception ex) { Log.Logger.Error(ex, $"Error opening solution: {SolutionPath.TargetPath}"); } }); var metaPath = this.WhenAnyValue(x => x.SelectedProjectPath.TargetPath) .Select(projPath => { try { return(Path.Combine(Path.GetDirectoryName(projPath) !, Constants.MetaFileName)); } catch (Exception) { return(string.Empty); } }) .Replay(1) .RefCount(); // Set up meta file sync metaPath .Select(path => { return(Noggog.ObservableExt.WatchFile(path) .StartWith(Unit.Default) .Select(_ => { try { return JsonConvert.DeserializeObject <PatcherCustomization>( File.ReadAllText(path), Execution.Constants.JsonSettings); } catch (Exception ex) { Logger.Error(ex, "Error reading in meta"); } return default(PatcherCustomization?); })); }) .Switch() .DistinctUntilChanged() .ObserveOnGui() .Subscribe(info => { if (info == null) { return; } if (info.Nickname != null) { this.Nickname = info.Nickname; } this.LongDescription = info.LongDescription ?? string.Empty; this.ShortDescription = info.OneLineDescription ?? string.Empty; this.Visibility = info.Visibility; this.Versioning = info.PreferredAutoVersioning; }) .DisposeWith(this); Observable.CombineLatest( this.WhenAnyValue(x => x.DisplayName), this.WhenAnyValue(x => x.ShortDescription), this.WhenAnyValue(x => x.LongDescription), this.WhenAnyValue(x => x.Visibility), this.WhenAnyValue(x => x.Versioning), metaPath, (nickname, shortDesc, desc, visibility, versioning, meta) => (nickname, shortDesc, desc, visibility, versioning, meta)) .DistinctUntilChanged() .Throttle(TimeSpan.FromMilliseconds(200), RxApp.MainThreadScheduler) .Skip(1) .Subscribe(x => { try { if (string.IsNullOrWhiteSpace(x.meta)) { return; } File.WriteAllText(x.meta, JsonConvert.SerializeObject( new PatcherCustomization() { OneLineDescription = x.shortDesc, LongDescription = x.desc, Visibility = x.visibility, Nickname = x.nickname, PreferredAutoVersioning = x.versioning }, Formatting.Indented, Execution.Constants.JsonSettings)); } catch (Exception ex) { Logger.Error(ex, "Error writing out meta"); } }) .DisposeWith(this); }