commit 2c26316ba6a9f6d475f7cf92d55bd74c0d2e9d78 parent 61350d7a1edd569b501472d6bde8c8cb2a8108f9 Author: Mikolaj Lenczewski <33129490+EnderRifter@users.noreply.github.com> Date: Sat, 17 Aug 2019 22:51:56 +0200 Implemented create and partial delete functionality. Added certain missing functionality to simulation view. Diffstat:
18 files changed, 1045 insertions(+), 49 deletions(-)
diff --git a/StarSim/StarSimGui/StarSimGui.csproj b/StarSim/StarSimGui/StarSimGui.csproj @@ -162,7 +162,7 @@ <Generator>MSBuild:Compile</Generator> </None> <None Update="Simulator.db"> - <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> <None Update="Views\MainWindow.xaml"> <Generator>MSBuild:Compile</Generator> diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/CreateSystemsViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/CreateSystemsViewModel.cs @@ -1,4 +1,9 @@ -using StarSimLib.Contexts; +using System; +using System.Linq; +using System.Windows.Input; +using ReactiveUI; +using StarSimLib.Contexts; +using StarSimLib.Models; namespace StarSimGui.ViewModels.Database_ViewModels { @@ -13,10 +18,26 @@ namespace StarSimGui.ViewModels.Database_ViewModels private readonly SimulatorContext dbContext; /// <summary> + /// Backing field for the <see cref="BodyMass"/> property. + /// </summary> + private double bodyMass; + + /// <summary> + /// Backing field for the <see cref="BodyName"/> property. + /// </summary> + private string bodyName; + + /// <summary> /// Initialises a new instance of the <see cref="CreateSystemsViewModel"/> class. /// </summary> public CreateSystemsViewModel() { + IObservable<bool> canCreate = this.WhenAnyValue(x => x.BodyName, x => x.BodyMass, + (name, mass) => !string.IsNullOrEmpty(name) && !string.IsNullOrWhiteSpace(name) && mass > 0); + + CreateBodyCommand = ReactiveCommand.Create(CreateBodyCommandImpl, canCreate); + + ResetBodyCommand = ReactiveCommand.Create(ResetBodyCommandImpl); } /// <summary> @@ -27,5 +48,114 @@ namespace StarSimGui.ViewModels.Database_ViewModels { dbContext = context; } + + /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseEdited; + + /// <summary> + /// The mass of the body to create. + /// </summary> + public double BodyMass + { + get + { + return bodyMass; + } + set + { + bodyMass = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// The name of the body to create. + /// </summary> + public string BodyName + { + get + { + return bodyName; + } + set + { + bodyName = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Command invoked whenever the user wants to add the currently edited body to the database. + /// </summary> + public ICommand CreateBodyCommand { get; } + + /// <summary> + /// Provides feedback about the changes made to the database. + /// </summary> + public string Feedback { get; private set; } + + /// <summary> + /// Command invoked whenever the user wants to reset the currently edited body. + /// </summary> + public ICommand ResetBodyCommand { get; } + + /// <summary> + /// Invoked whenever the user wants to add the currently edited body to the database. + /// </summary> + private void CreateBodyCommandImpl() + { + if (dbContext.Bodies.Any(body => body.Name.Equals(BodyName))) + { + Feedback = "Body with the same name already exists in the database."; + this.RaisePropertyChanged(nameof(Feedback)); + } + + try + { + Body lastBody = dbContext.Bodies.OrderBy(body => body.Id).Last(); + + Body newBody = new Body(lastBody.Id + 1, BodyName, BodyMass); + + dbContext.Bodies.Add(newBody); + + Feedback = "Body was successfully added to the database."; + this.RaisePropertyChanged(nameof(Feedback)); + + OnDatabaseEdited(); + } + catch (Exception) + { + Feedback = "Body could not be added to the database."; + this.RaisePropertyChanged(nameof(Feedback)); + } + } + + /// <summary> + /// Invokes the <see cref="DatabaseEdited"/> event. + /// </summary> + private void OnDatabaseEdited() + { + DatabaseEdited?.Invoke(); + } + + /// <summary> + /// Invoked whenever the user wants to reset the currently edited body. + /// </summary> + private void ResetBodyCommandImpl() + { + BodyName = ""; + BodyMass = 0; + Feedback = ""; + this.RaisePropertyChanged(nameof(Feedback)); + } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/CreateUsersViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/CreateUsersViewModel.cs @@ -1,4 +1,12 @@ -using StarSimLib.Contexts; +using System; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Input; +using ReactiveUI; +using StarSimLib.Contexts; +using StarSimLib.Cryptography; +using StarSimLib.Models; namespace StarSimGui.ViewModels.Database_ViewModels { @@ -13,10 +21,80 @@ namespace StarSimGui.ViewModels.Database_ViewModels private readonly SimulatorContext dbContext; /// <summary> + /// Backing field for the <see cref="Email"/> property. + /// </summary> + private string email; + + /// <summary> + /// Backing field for the <see cref="Password"/> property. + /// </summary> + private string password; + + /// <summary> + /// Backing field for the <see cref="Privileges"/> property. + /// </summary> + private UserPrivileges privileges; + + /// <summary> + /// Backing field for the <see cref="Username"/> property. + /// </summary> + private string username; + + /// <summary> + /// The list of accepted email providers. + /// </summary> + public static readonly string[] AcceptedEmailProviders = { + "gmail.com", + "hotmail.com", + "yahoo.com" + }; + + /// <summary> /// Initialises a new instance of the <see cref="CreateUsersViewModel"/> class. /// </summary> public CreateUsersViewModel() { + privileges = UserPrivileges.Default; + + #region Regex Pattern Builder + + StringBuilder regexBuilder = new StringBuilder(@"\w[^@]@("); + + for (int i = 0; i < AcceptedEmailProviders.Length; i++) + { + regexBuilder.Append(i == 0 + ? $"{AcceptedEmailProviders[i].Replace(".", @"\.")}" + : $"|{AcceptedEmailProviders[i].Replace(".", @"\.")}"); + } + + regexBuilder.Append(")$"); + + #endregion Regex Pattern Builder + + Regex emailValidationRegex = new Regex(regexBuilder.ToString(), RegexOptions.Compiled); + + IObservable<bool> canCreate = this.WhenAnyValue(x => x.Username, x => x.Email, x => x.Password, + (username, email, password) => + { + // we must have a valid password and username to create a user + if (!string.IsNullOrEmpty(username) && !string.IsNullOrWhiteSpace(username) && + !string.IsNullOrEmpty(password) && !string.IsNullOrWhiteSpace(password)) + { + // if the optional email is given, then it must match the email validation regex + if (!string.IsNullOrEmpty(email) && !string.IsNullOrWhiteSpace(email)) + { + return emailValidationRegex.IsMatch(email); + } + + return true; + } + + return false; + }); + + CreateUserCommand = ReactiveCommand.Create(CreateUserCommandImpl, canCreate); + + ResetUserCommand = ReactiveCommand.Create(ResetUserCommandImpl); } /// <summary> @@ -27,5 +105,159 @@ namespace StarSimGui.ViewModels.Database_ViewModels { dbContext = context; } + + /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseEdited; + + /// <summary> + /// Command invoked whenever the user wants to add the currently edited user to the database. + /// </summary> + public ICommand CreateUserCommand { get; } + + /// <summary> + /// The email of the user to create. + /// </summary> + public string Email + { + get + { + return email; + } + set + { + email = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Provides feedback about the changes made to the database. + /// </summary> + public string Feedback { get; private set; } + + /// <summary> + /// The password of the user to create. + /// </summary> + public string Password + { + get + { + return password; + } + set + { + password = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// The possible <see cref="UserPrivileges"/> that a user can have. + /// </summary> + public UserPrivileges[] PossiblePrivileges + { + get { return new[] { UserPrivileges.Default, UserPrivileges.Publisher, UserPrivileges.Admin }; } + } + + /// <summary> + /// The privileges of the user to create. + /// </summary> + public UserPrivileges Privileges + { + get + { + return privileges; + } + set + { + privileges = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Command invoked whenever the user wants to reset the currently edited user. + /// </summary> + public ICommand ResetUserCommand { get; } + + /// <summary> + /// The username of the user to create. + /// </summary> + public string Username + { + get + { + return username; + } + set + { + username = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Invoked whenever the user wants to add the currently edited user to the database. + /// </summary> + private void CreateUserCommandImpl() + { + if (dbContext.Users.Any(user => user.Username.Equals(Username))) + { + Feedback = "User with the same username already exists in the database."; + this.RaisePropertyChanged(nameof(Feedback)); + } + + try + { + User lastUser = dbContext.Users.OrderBy(user => user.Id).Last(); + + byte[] passwordBytes = CryptographyHelper.StringToBytes(Password); + byte[] passwordSalt = CryptographyHelper.GenerateSalt(); + byte[] passwordHash = CryptographyHelper.GenerateHash(passwordBytes, passwordSalt); + + User newUser = new User(lastUser.Id + 1, Username, Privileges, passwordHash, passwordSalt, Email); + + dbContext.Users.Add(newUser); + + Feedback = "User was successfully added to the database."; + this.RaisePropertyChanged(nameof(Feedback)); + + OnDatabaseEdited(); + } + catch (Exception) + { + Feedback = "User could not be added to the database."; + this.RaisePropertyChanged(nameof(Feedback)); + } + } + + /// <summary> + /// Invokes the <see cref="DatabaseEdited"/> event. + /// </summary> + private void OnDatabaseEdited() + { + DatabaseEdited?.Invoke(); + } + + /// <summary> + /// Invoked whenever the user wants to reset the currently edited user. + /// </summary> + private void ResetUserCommandImpl() + { + Username = ""; + Password = ""; + Email = ""; + Feedback = ""; + this.RaisePropertyChanged(nameof(Feedback)); + } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/DeleteSystemsViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/DeleteSystemsViewModel.cs @@ -1,4 +1,9 @@ -using StarSimLib.Contexts; +using System; +using System.Windows.Input; +using DynamicData.Binding; +using ReactiveUI; +using StarSimLib.Contexts; +using StarSimLib.Models; namespace StarSimGui.ViewModels.Database_ViewModels { @@ -13,19 +18,286 @@ namespace StarSimGui.ViewModels.Database_ViewModels private readonly SimulatorContext dbContext; /// <summary> - /// Initialises a new instance of the <see cref="DeleteSystemsViewModel"/> class. + /// Backing field for the <see cref="Bodies"/> property. + /// </summary> + private IObservableCollection<Body> bodies; + + /// <summary> + /// Backing field for the <see cref="Bodies"/> property. + /// </summary> + private IObservableCollection<PublishedSystem> publishedSystems; + + /// <summary> + /// Backing field for the <see cref="SelectedBody"/> property. + /// </summary> + private Body selectedBody; + + /// <summary> + /// Backing field for the <see cref="SelectedPublishedSystem"/> property. + /// </summary> + private PublishedSystem selectedPublishedSystem; + + /// <summary> + /// Backing field for the <see cref="SelectedSystem"/> property. + /// </summary> + private StarSimLib.Models.System selectedSystem; + + /// <summary> + /// Backing field for the <see cref="Bodies"/> property. + /// </summary> + private IObservableCollection<StarSimLib.Models.System> systems; + + /// <summary> + /// Initialises a new instance of the <see cref="DeleteUsersViewModel"/> class. /// </summary> public DeleteSystemsViewModel() { + bodies = new ObservableCollectionExtended<Body>(); + + systems = new ObservableCollectionExtended<StarSimLib.Models.System>(); + + publishedSystems = new ObservableCollectionExtended<PublishedSystem>(); + + IObservable<bool> canRemoveBody = this.WhenAnyValue(x => x.SelectedBody, selector: user => user != null); + + RemoveBodyCommand = ReactiveCommand.Create(RemoveBodyCommandImpl, canRemoveBody); + + IObservable<bool> canRemoveSystem = + this.WhenAnyValue(x => x.SelectedSystem, selector: system => system != null); + + RemoveSystemCommand = ReactiveCommand.Create(RemoveSystemCommandImpl, canRemoveSystem); + + IObservable<bool> canRemovePublishedSystem = this.WhenAnyValue(x => x.SelectedPublishedSystem, + selector: publishedSystem => publishedSystem != null); + + RemovePublishedSystemCommand = ReactiveCommand.Create(RemovePublishedSystemCommandImpl, canRemovePublishedSystem); } /// <summary> - /// Initialises a new instance of the <see cref="DeleteSystemsViewModel"/> class. + /// Initialises a new instance of the <see cref="DeleteUsersViewModel"/> class. /// </summary> /// <param name="context">The <see cref="SimulatorContext"/> instance in which program data is stored.</param> public DeleteSystemsViewModel(in SimulatorContext context) : this() { dbContext = context; + + Bodies.Load(dbContext.Bodies); + + Systems.Load(dbContext.Systems); + + PublishedSystems.Load(dbContext.PublishedSystems); + } + + /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseEdited; + + /// <summary> + /// The users held in the database. + /// </summary> + public IObservableCollection<Body> Bodies + { + get + { + return bodies; + } + set + { + bodies = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Provides feedback about the changes made to the body database. + /// </summary> + public string BodyFeedback { get; private set; } + + /// <summary> + /// Provides feedback about the changes made to the published system database. + /// </summary> + public string PublishedSystemFeedback { get; private set; } + + /// <summary> + /// The users held in the database. + /// </summary> + public IObservableCollection<PublishedSystem> PublishedSystems + { + get + { + return publishedSystems; + } + set + { + publishedSystems = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Command invoked whenever the user wants to remove the currently selected body from the database. + /// </summary> + public ICommand RemoveBodyCommand { get; } + + /// <summary> + /// Command invoked whenever the user wants to remove the currently selected published system from the database. + /// </summary> + public ICommand RemovePublishedSystemCommand { get; } + + /// <summary> + /// Command invoked whenever the user wants to remove the currently selected system from the database. + /// </summary> + public ICommand RemoveSystemCommand { get; } + + /// <summary> + /// The currently selected body. + /// </summary> + public Body SelectedBody + { + get + { + return selectedBody; + } + set + { + selectedBody = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// The currently selected published system. + /// </summary> + public PublishedSystem SelectedPublishedSystem + { + get + { + return selectedPublishedSystem; + } + set + { + selectedPublishedSystem = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// The currently selected system. + /// </summary> + public StarSimLib.Models.System SelectedSystem + { + get + { + return selectedSystem; + } + set + { + selectedSystem = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Provides feedback about the changes made to the system database. + /// </summary> + public string SystemFeedback { get; private set; } + + /// <summary> + /// The users held in the database. + /// </summary> + public IObservableCollection<StarSimLib.Models.System> Systems + { + get + { + return systems; + } + set + { + systems = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Invokes the <see cref="DatabaseEdited"/> event. + /// </summary> + private void OnDatabaseEdited() + { + DatabaseEdited?.Invoke(); + } + + /// <summary> + /// Invoked whenever the user wants to remove the currently selected body from the database. + /// </summary> + private void RemoveBodyCommandImpl() + { + try + { + dbContext.Bodies.Remove(SelectedBody); + + BodyFeedback = "Body was successfully removed from the database."; + this.RaisePropertyChanged(nameof(BodyFeedback)); + + OnDatabaseEdited(); + } + catch (Exception) + { + BodyFeedback = "Body could not be removed from the database."; + this.RaisePropertyChanged(nameof(BodyFeedback)); + } + } + + /// <summary> + /// Invoked whenever the user wants to remove the currently selected published system from the database. + /// </summary> + private void RemovePublishedSystemCommandImpl() + { + try + { + dbContext.PublishedSystems.Remove(SelectedPublishedSystem); + + PublishedSystemFeedback = "Published system was successfully removed from the database."; + this.RaisePropertyChanged(nameof(PublishedSystemFeedback)); + + OnDatabaseEdited(); + } + catch (Exception) + { + PublishedSystemFeedback = "Published system could not be removed from the database."; + this.RaisePropertyChanged(nameof(PublishedSystemFeedback)); + } + } + + /// <summary> + /// Invoked whenever the user wants to remove the currently selected system from the database. + /// </summary> + private void RemoveSystemCommandImpl() + { + try + { + dbContext.Systems.Remove(SelectedSystem); + + SystemFeedback = "System was successfully removed from the database."; + this.RaisePropertyChanged(nameof(SystemFeedback)); + + OnDatabaseEdited(); + } + catch (Exception) + { + SystemFeedback = "System could not be removed from the database."; + this.RaisePropertyChanged(nameof(SystemFeedback)); + } + } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + Bodies.Load(dbContext.Bodies); + Systems.Load(dbContext.Systems); + PublishedSystems.Load(dbContext.PublishedSystems); } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/DeleteUsersViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/DeleteUsersViewModel.cs @@ -1,4 +1,9 @@ -using StarSimLib.Contexts; +using System; +using System.Windows.Input; +using DynamicData.Binding; +using ReactiveUI; +using StarSimLib.Contexts; +using StarSimLib.Models; namespace StarSimGui.ViewModels.Database_ViewModels { @@ -13,10 +18,25 @@ namespace StarSimGui.ViewModels.Database_ViewModels private readonly SimulatorContext dbContext; /// <summary> + /// Backing field for the <see cref="SelectedUser"/> property. + /// </summary> + private User selectedUser; + + /// <summary> + /// Backing field for the <see cref="Users"/> property. + /// </summary> + private IObservableCollection<User> users; + + /// <summary> /// Initialises a new instance of the <see cref="DeleteUsersViewModel"/> class. /// </summary> public DeleteUsersViewModel() { + users = new ObservableCollectionExtended<User>(); + + IObservable<bool> canRemove = this.WhenAnyValue(x => x.SelectedUser, selector: user => user != null); + + RemoveUserCommand = ReactiveCommand.Create(RemoveUserCommandImpl, canRemove); } /// <summary> @@ -26,6 +46,92 @@ namespace StarSimGui.ViewModels.Database_ViewModels public DeleteUsersViewModel(in SimulatorContext context) : this() { dbContext = context; + + Users.Load(dbContext.Users); + } + + /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseEdited; + + /// <summary> + /// Provides feedback about the changes made to the database. + /// </summary> + public string Feedback { get; private set; } + + /// <summary> + /// Command invoked whenever the user wants to remove the currently selected user from the database. + /// </summary> + public ICommand RemoveUserCommand { get; } + + /// <summary> + /// The currently selected user. + /// </summary> + public User SelectedUser + { + get + { + return selectedUser; + } + set + { + selectedUser = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// The users held in the database. + /// </summary> + public IObservableCollection<User> Users + { + get + { + return users; + } + set + { + users = value; + this.RaisePropertyChanged(); + } + } + + /// <summary> + /// Invokes the <see cref="DatabaseEdited"/> event. + /// </summary> + private void OnDatabaseEdited() + { + DatabaseEdited?.Invoke(); + } + + /// <summary> + /// Invoked whenever the user wants to remove the currently selected user from the database. + /// </summary> + private void RemoveUserCommandImpl() + { + try + { + dbContext.Users.Remove(SelectedUser); + + Feedback = "User was successfully removed from the database."; + this.RaisePropertyChanged(nameof(Feedback)); + + OnDatabaseEdited(); + } + catch (Exception) + { + Feedback = "User could not be removed from the database."; + this.RaisePropertyChanged(nameof(Feedback)); + } + } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + Users.Load(dbContext.Users); } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/ReadSystemsViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/ReadSystemsViewModel.cs @@ -142,5 +142,14 @@ namespace StarSimGui.ViewModels.Database_ViewModels this.RaisePropertyChanged(); } } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + Bodies.Load(dbContext.Bodies); + Systems.Load(dbContext.Systems); + } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/ReadUsersViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/ReadUsersViewModel.cs @@ -142,5 +142,14 @@ namespace StarSimGui.ViewModels.Database_ViewModels this.RaisePropertyChanged(); } } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + PublishedSystems.Load(dbContext.PublishedSystems); + Users.Load(dbContext.Users); + } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/UpdateSystemsViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/UpdateSystemsViewModel.cs @@ -1,4 +1,5 @@ -using StarSimLib.Contexts; +using System; +using StarSimLib.Contexts; namespace StarSimGui.ViewModels.Database_ViewModels { @@ -27,5 +28,25 @@ namespace StarSimGui.ViewModels.Database_ViewModels { dbContext = context; } + + /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseEdited; + + /// <summary> + /// Invokes the <see cref="DatabaseEdited"/> event. + /// </summary> + private void OnDatabaseEdited() + { + DatabaseEdited?.Invoke(); + } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/Database ViewModels/UpdateUsersViewModel.cs b/StarSim/StarSimGui/ViewModels/Database ViewModels/UpdateUsersViewModel.cs @@ -1,4 +1,5 @@ -using StarSimLib.Contexts; +using System; +using StarSimLib.Contexts; namespace StarSimGui.ViewModels.Database_ViewModels { @@ -27,5 +28,25 @@ namespace StarSimGui.ViewModels.Database_ViewModels { dbContext = context; } + + /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseEdited; + + /// <summary> + /// Invokes the <see cref="DatabaseEdited"/> event. + /// </summary> + private void OnDatabaseEdited() + { + DatabaseEdited?.Invoke(); + } + + /// <summary> + /// Refreshed the database sources for this view model. + /// </summary> + internal void HandleDatabaseRefresh() + { + } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/DatabaseViewModel.cs b/StarSim/StarSimGui/ViewModels/DatabaseViewModel.cs @@ -1,11 +1,14 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Windows.Input; using DynamicData.Binding; using Microsoft.EntityFrameworkCore; using ReactiveUI; using StarSimGui.ViewModels.Database_ViewModels; using StarSimLib.Contexts; using StarSimLib.Models; +using Console = System.Console; namespace StarSimGui.ViewModels { @@ -84,6 +87,13 @@ namespace StarSimGui.ViewModels updateSystemsViewModel = new UpdateSystemsViewModel(); updateUsersViewModel = new UpdateUsersViewModel(); + + createSystemsViewModel.DatabaseEdited += OnDatabaseEdited; + createUsersViewModel.DatabaseEdited += OnDatabaseEdited; + deleteSystemsViewModel.DatabaseEdited += OnDatabaseEdited; + deleteUsersViewModel.DatabaseEdited += OnDatabaseEdited; + updateSystemsViewModel.DatabaseEdited += OnDatabaseEdited; + updateUsersViewModel.DatabaseEdited += OnDatabaseEdited; } /// <summary> @@ -94,6 +104,14 @@ namespace StarSimGui.ViewModels { dbContext = context; + // unbind handlers to ensure that no memory leaks occur upon garbage collection of old objects + createSystemsViewModel.DatabaseEdited -= OnDatabaseEdited; + createUsersViewModel.DatabaseEdited -= OnDatabaseEdited; + deleteSystemsViewModel.DatabaseEdited -= OnDatabaseEdited; + deleteUsersViewModel.DatabaseEdited -= OnDatabaseEdited; + updateSystemsViewModel.DatabaseEdited -= OnDatabaseEdited; + updateUsersViewModel.DatabaseEdited -= OnDatabaseEdited; + createSystemsViewModel = new CreateSystemsViewModel(in context); createUsersViewModel = new CreateUsersViewModel(in context); @@ -109,6 +127,35 @@ namespace StarSimGui.ViewModels updateSystemsViewModel = new UpdateSystemsViewModel(in context); updateUsersViewModel = new UpdateUsersViewModel(in context); + + // binding of new handlers + createSystemsViewModel.DatabaseEdited += OnDatabaseEdited; + createUsersViewModel.DatabaseEdited += OnDatabaseEdited; + deleteSystemsViewModel.DatabaseEdited += OnDatabaseEdited; + deleteUsersViewModel.DatabaseEdited += OnDatabaseEdited; + updateSystemsViewModel.DatabaseEdited += OnDatabaseEdited; + updateUsersViewModel.DatabaseEdited += OnDatabaseEdited; + } + + /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseUpdated; + + /// <summary> + /// Exposes the <see cref="createSystemsViewModel"/> field to the view. + /// </summary> + public CreateSystemsViewModel CreateSystemsViewModel + { + get { return createSystemsViewModel; } + } + + /// <summary> + /// Exposes the <see cref="createUsersViewModel"/> field to the view. + /// </summary> + public CreateUsersViewModel CreateUsersViewModel + { + get { return createUsersViewModel; } } /// <summary> @@ -135,6 +182,22 @@ namespace StarSimGui.ViewModels } /// <summary> + /// Exposes the <see cref="deleteSystemsViewModel"/> field to the view. + /// </summary> + public DeleteSystemsViewModel DeleteSystemsViewModel + { + get { return deleteSystemsViewModel; } + } + + /// <summary> + /// Exposes the <see cref="deleteUsersViewModel"/> field to the view. + /// </summary> + public DeleteUsersViewModel DeleteUsersViewModel + { + get { return deleteUsersViewModel; } + } + + /// <summary> /// Whether the currently logged in user has administrator privileges. /// </summary> public bool IsAdmin @@ -175,6 +238,49 @@ namespace StarSimGui.ViewModels } /// <summary> + /// Exposes the <see cref="updateSystemsViewModel"/> field to the view. + /// </summary> + public UpdateSystemsViewModel UpdateSystemsViewModel + { + get { return updateSystemsViewModel; } + } + + /// <summary> + /// Exposes the <see cref="updateUsersViewModel"/> field to the view. + /// </summary> + public UpdateUsersViewModel UpdateUsersViewModel + { + get { return updateUsersViewModel; } + } + + /// <summary> + /// Invokes the <see cref="DatabaseUpdated"/> event. + /// </summary> + private void OnDatabaseEdited() + { + DatabaseUpdated?.Invoke(); + } + + /// <summary> + /// Invoked whenever the user wants to refresh all database sources. + /// </summary> + internal void RefreshDBSources() + { + Console.WriteLine("Refreshing database sources"); + + CreateSystemsViewModel.HandleDatabaseRefresh(); + CreateUsersViewModel.HandleDatabaseRefresh(); + DeleteSystemsViewModel.HandleDatabaseRefresh(); + DeleteUsersViewModel.HandleDatabaseRefresh(); + ReadSystemsViewModel.HandleDatabaseRefresh(); + ReadUsersViewModel.HandleDatabaseRefresh(); + UpdateSystemsViewModel.HandleDatabaseRefresh(); + UpdateUsersViewModel.HandleDatabaseRefresh(); + + Console.WriteLine("Refreshed database sources"); + } + + /// <summary> /// Handles the <see cref="UserLoginViewModel.LoggedIn"/> event. /// </summary> /// <param name="newUser">The user which logged in.</param> diff --git a/StarSim/StarSimGui/ViewModels/MainWindowViewModel.cs b/StarSim/StarSimGui/ViewModels/MainWindowViewModel.cs @@ -1,4 +1,5 @@ -using ReactiveUI; +using System; +using ReactiveUI; using StarSimLib.Contexts; using StarSimLib.Models; @@ -10,6 +11,11 @@ namespace StarSimGui.ViewModels public class MainWindowViewModel : ViewModelBase { /// <summary> + /// The database context in which the program should store data and which it should query to fetch data. + /// </summary> + private readonly SimulatorContext simulatorContext; + + /// <summary> /// Backing field for the <see cref="CurrentUser"/> property. /// </summary> private User currentUser; @@ -19,15 +25,17 @@ namespace StarSimGui.ViewModels /// </summary> public MainWindowViewModel() { - SimulatorContext = new SimulatorContext(); + simulatorContext = new SimulatorContext(); - DatabaseViewModel = new DatabaseViewModel(SimulatorContext); + simulatorContext.ChangeTracker.AutoDetectChangesEnabled = true; - OverviewViewModel = new OverviewViewModel(SimulatorContext); + DatabaseViewModel = new DatabaseViewModel(simulatorContext); - SimulationViewModel = new SimulationViewModel(SimulatorContext); + OverviewViewModel = new OverviewViewModel(simulatorContext); - UserLoginViewModel = new UserLoginViewModel(SimulatorContext); + SimulationViewModel = new SimulationViewModel(simulatorContext); + + UserLoginViewModel = new UserLoginViewModel(simulatorContext); UserLoginViewModel.LoggedIn += user => CurrentUser = user; UserLoginViewModel.LoggedIn += DatabaseViewModel.HandleLogin; @@ -39,6 +47,9 @@ namespace StarSimGui.ViewModels UserLoginViewModel.LoggedOut += OverviewViewModel.HandleLogout; UserLoginViewModel.LoggedOut += SimulationViewModel.HandleLogout; + SimulationViewModel.DatabaseUpdated += HandleDatabaseUpdated; + DatabaseViewModel.DatabaseUpdated += HandleDatabaseUpdated; + #if DEBUG // simulate a login to accelerate development of the application, as the implemented cryptographic features // of the login system make it slow to solve for the password hash. furthermore loading the database takes @@ -48,11 +59,6 @@ namespace StarSimGui.ViewModels } /// <summary> - /// The database context in which the program should store data and which it should query to fetch data. - /// </summary> - private SimulatorContext SimulatorContext { get; } - - /// <summary> /// The currently logged in user. /// </summary> public User CurrentUser @@ -87,5 +93,15 @@ namespace StarSimGui.ViewModels /// Represents the user login view. /// </summary> public UserLoginViewModel UserLoginViewModel { get; set; } + + /// <summary> + /// Handles the DatabaseEdited event. + /// </summary> + private void HandleDatabaseUpdated() + { + Console.WriteLine($"Database has tracked changes?: {simulatorContext.ChangeTracker.HasChanges()}"); + Console.WriteLine($"Saved {simulatorContext.SaveChanges()} changed entities"); + DatabaseViewModel.RefreshDBSources(); + } } } \ No newline at end of file diff --git a/StarSim/StarSimGui/ViewModels/SimulationViewModel.cs b/StarSim/StarSimGui/ViewModels/SimulationViewModel.cs @@ -8,7 +8,6 @@ using Microsoft.EntityFrameworkCore; using ReactiveUI; using SFML.Graphics; using SFML.Window; -using SharpDX.Win32; using StarSimGui.Source; using StarSimLib; using StarSimLib.Contexts; @@ -242,6 +241,11 @@ namespace StarSimGui.ViewModels #endregion Commands /// <summary> + /// Signifies that the database should be updated. + /// </summary> + public event Action DatabaseUpdated; + + /// <summary> /// The <see cref="Body"/> instance currently selected for editing or viewing. /// </summary> public BodyDummy CurrentBody @@ -274,6 +278,14 @@ namespace StarSimGui.ViewModels } /// <summary> + /// Whether the current user can publish systems. + /// </summary> + public bool IsPublisher + { + get { return (CurrentUser.Privileges & UserPrivileges.Publisher) == UserPrivileges.Publisher; } + } + + /// <summary> /// Whether the currently selected published system is null. /// </summary> public bool IsSelectedPublishedSystemNull @@ -394,6 +406,14 @@ namespace StarSimGui.ViewModels } } + /// <summary> + /// Invokes the <see cref="DatabaseUpdated"/> event. + /// </summary> + private void OnDatabaseUpdated() + { + DatabaseUpdated?.Invoke(); + } + #region Command Implementations /// <summary> @@ -548,7 +568,7 @@ namespace StarSimGui.ViewModels dbContext.BodyToSystemJoins.AddRange(joins); dbContext.PublishedSystems.Add(newPublishedSystem); - dbContext.SaveChanges(); + OnDatabaseUpdated(); PublishedSystems.Load(dbContext.PublishedSystems); @@ -618,6 +638,8 @@ namespace StarSimGui.ViewModels public void HandleLogin(User newUser) { CurrentUser = newUser; + + this.RaisePropertyChanged(nameof(IsPublisher)); } /// <summary> diff --git a/StarSim/StarSimGui/Views/Database Views/CreateSystems.xaml b/StarSim/StarSimGui/Views/Database Views/CreateSystems.xaml @@ -12,7 +12,15 @@ </Design.DataContext> <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2"> - <Grid ColumnDefinitions="*,*,*,*" RowDefinitions="*,*,*,*"> - </Grid> + <StackPanel> + <TextBlock HorizontalAlignment="Center">Create A Body:</TextBlock> + <TextBlock HorizontalAlignment="Center">Body Name:</TextBlock> + <TextBox HorizontalAlignment="Stretch" Text="{Binding BodyName, Mode=TwoWay}"></TextBox> + <TextBlock HorizontalAlignment="Center">Body Mass:</TextBlock> + <TextBox HorizontalAlignment="Stretch" Text="{Binding BodyMass, Mode=TwoWay}"></TextBox> + <TextBlock HorizontalAlignment="Center" Text="{Binding Feedback}"></TextBlock> + <Button Command="{Binding CreateBodyCommand}" Content="Create Body" HorizontalAlignment="Stretch"></Button> + <Button Command="{Binding ResetBodyCommand}" Content="Reset Body" HorizontalAlignment="Stretch"></Button> + </StackPanel> </Border> </UserControl> \ No newline at end of file diff --git a/StarSim/StarSimGui/Views/Database Views/CreateUsers.xaml b/StarSim/StarSimGui/Views/Database Views/CreateUsers.xaml @@ -7,12 +7,24 @@ mc:Ignorable="d" x:Class="StarSimGui.Views.Database_Views.CreateUser"> -<Design.DataContext> -<vm:CreateUsersViewModel /> -</Design.DataContext> + <Design.DataContext> + <vm:CreateUsersViewModel /> + </Design.DataContext> -<Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2"> - <Grid ColumnDefinitions="*,*,*,*" RowDefinitions="*,*,*,*"> - </Grid> + <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2"> + <StackPanel> + <TextBlock HorizontalAlignment="Center">Create A User:</TextBlock> + <TextBlock HorizontalAlignment="Center">Username:</TextBlock> + <TextBox HorizontalAlignment="Stretch" Text="{Binding Username, Mode=TwoWay}"></TextBox> + <TextBlock HorizontalAlignment="Center">Email:</TextBlock> + <TextBox HorizontalAlignment="Stretch" Text="{Binding Email, Mode=TwoWay}"></TextBox> + <TextBlock HorizontalAlignment="Center">Password:</TextBlock> + <TextBox HorizontalAlignment="Stretch" Text="{Binding Password, Mode=TwoWay}"></TextBox> + <TextBlock HorizontalAlignment="Center">Privileges:</TextBlock> + <ListBox Items="{Binding PossiblePrivileges}" SelectedItem="{Binding Privileges, Mode=OneWayToSource}"></ListBox> + <TextBlock HorizontalAlignment="Center" Text="{Binding Feedback}"></TextBlock> + <Button Command="{Binding CreateUserCommand}" Content="Create User" HorizontalAlignment="Stretch"></Button> + <Button Command="{Binding ResetUserCommand}" Content="Reset User" HorizontalAlignment="Stretch"></Button> + </StackPanel> </Border> </UserControl> \ No newline at end of file diff --git a/StarSim/StarSimGui/Views/Database Views/DeleteSystems.xaml b/StarSim/StarSimGui/Views/Database Views/DeleteSystems.xaml @@ -12,7 +12,31 @@ </Design.DataContext> <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2"> - <Grid ColumnDefinitions="*,*,*,*" RowDefinitions="*,*,*,*"> + <Grid ColumnDefinitions="*,*,*" RowDefinitions="*,*,*,*"> + <ListBox Items="{Binding Bodies}" SelectedItem="{Binding SelectedBody, Mode=TwoWay}" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="3"> + </ListBox> + <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="3"> + <StackPanel> + <TextBlock Text="{Binding BodyFeedback}"></TextBlock> + <Button Command="{Binding RemoveBodyCommand}" Content="Remove Body"></Button> + </StackPanel> + </Border> + <ListBox Items="{Binding Systems}" SelectedItem="{Binding SelectedSystem, Mode=TwoWay}" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="3"> + </ListBox> + <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="3"> + <StackPanel> + <TextBlock Text="{Binding SystemFeedback}"></TextBlock> + <Button Command="{Binding RemoveSystemCommand}" Content="Remove System"></Button> + </StackPanel> + </Border> + <ListBox Items="{Binding PublishedSystems}" SelectedItem="{Binding SelectedPublishedSystem, Mode=TwoWay}" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="3"> + </ListBox> + <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="3"> + <StackPanel> + <TextBlock Text="{Binding PublishedSystemFeedback}"></TextBlock> + <Button Command="{Binding RemovePublishedSystemCommand}" Content="Remove Published System"></Button> + </StackPanel> + </Border> </Grid> </Border> </UserControl> \ No newline at end of file diff --git a/StarSim/StarSimGui/Views/Database Views/DeleteUsers.xaml b/StarSim/StarSimGui/Views/Database Views/DeleteUsers.xaml @@ -13,6 +13,14 @@ <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2"> <Grid ColumnDefinitions="*,*,*,*" RowDefinitions="*,*,*,*"> + <ListBox Items="{Binding Users}" SelectedItem="{Binding SelectedUser, Mode=TwoWay}" Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="0" Grid.RowSpan="3"> + </ListBox> + <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2" Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="3"> + <StackPanel> + <TextBlock Text="{Binding Feedback}"></TextBlock> + <Button Command="{Binding RemoveUserCommand}" Content="Remove User"></Button> + </StackPanel> + </Border> </Grid> </Border> </UserControl> \ No newline at end of file diff --git a/StarSim/StarSimGui/Views/Database.xaml b/StarSim/StarSimGui/Views/Database.xaml @@ -15,29 +15,29 @@ <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="2" Margin="2"> <TabControl> - <TabItem Header="Create Users" IsVisible="{Binding IsAdmin}"> - <subviews:CreateUsers DataContext="{Binding}" /> + <TabItem Header="Create Users" IsVisible="{Binding IsAdmin}" IsEnabled="{Binding IsAdmin}"> + <subviews:CreateUsers DataContext="{Binding CreateUsersViewModel}" IsVisible="{Binding $parent.DataContext.IsAdmin}" IsEnabled="{Binding $parent.DataContext.IsAdmin}" /> </TabItem> - <TabItem Header="Create Bodies/Systems" IsVisible="{Binding IsPublisher}"> - <subviews:CreateSystems DataContext="{Binding}" /> + <TabItem Header="Create Bodies" IsVisible="{Binding IsPublisher}" IsEnabled="{Binding IsPublisher}"> + <subviews:CreateSystems DataContext="{Binding CreateSystemsViewModel}" IsVisible="{Binding $parent.DataContext.IsPublisher}" IsEnabled="{Binding $parent.DataContext.IsPublisher}" /> </TabItem> - <TabItem Header="View Users" IsVisible="{Binding IsDefault}"> - <subviews:ReadUsers DataContext="{Binding ReadUsersViewModel}" /> + <TabItem Header="View Users" IsVisible="{Binding IsDefault}" IsEnabled="{Binding IsDefault}"> + <subviews:ReadUsers DataContext="{Binding ReadUsersViewModel}" IsVisible="{Binding $parent.DataContext.IsDefault}" IsEnabled="{Binding $parent.DataContext.IsDefault}" /> </TabItem> - <TabItem Header="View Bodies/Systems" IsVisible="{Binding IsDefault}"> - <subviews:ReadSystems DataContext="{Binding ReadSystemsViewModel}" /> + <TabItem Header="View Bodies/Systems" IsVisible="{Binding IsDefault}" IsEnabled="{Binding IsDefault}"> + <subviews:ReadSystems DataContext="{Binding ReadSystemsViewModel}" IsVisible="{Binding $parent.DataContext.IsDefault}" IsEnabled="{Binding $parent.DataContext.IsDefault}" /> </TabItem> - <TabItem Header="Update Users" IsVisible="{Binding IsAdmin}"> - <subviews:UpdateUsers DataContext="{Binding}" /> + <TabItem Header="Update Users" IsVisible="{Binding IsAdmin}" IsEnabled="{Binding IsAdmin}"> + <subviews:UpdateUsers DataContext="{Binding UpdateUsersViewModel}" IsVisible="{Binding $parent.DataContext.IsAdmin}" IsEnabled="{Binding $parent.DataContext.IsAdmin}" /> </TabItem> - <TabItem Header="Update Bodies/Systems" IsVisible="{Binding IsPublisher}"> - <subviews:UpdateSystems DataContext="{Binding}" /> + <TabItem Header="Update Bodies/Systems" IsVisible="{Binding IsPublisher}" IsEnabled="{Binding IsPublisher}"> + <subviews:UpdateSystems DataContext="{Binding UpdateSystemsViewModel}" IsVisible="{Binding $parent.DataContext.IsPublisher}" IsEnabled="{Binding $parent.DataContext.IsPublisher}" /> </TabItem> - <TabItem Header="Delete Users" IsVisible="{Binding IsAdmin}"> - <subviews:DeleteUsers DataContext="{Binding}" /> + <TabItem Header="Delete Users" IsVisible="{Binding IsAdmin}" IsEnabled="{Binding IsAdmin}"> + <subviews:DeleteUsers DataContext="{Binding DeleteUsersViewModel}" IsVisible="{Binding $parent.DataContext.IsAdmin}" IsEnabled="{Binding $parent.DataContext.IsAdmin}" /> </TabItem> - <TabItem Header="Delete Bodies/Systems" IsVisible="{Binding IsPublisher}"> - <subviews:DeleteSystems DataContext="{Binding}" /> + <TabItem Header="Delete Bodies/Systems" IsVisible="{Binding IsPublisher}" IsEnabled="{Binding IsPublisher}"> + <subviews:DeleteSystems DataContext="{Binding DeleteSystemsViewModel}" IsVisible="{Binding $parent.DataContext.IsPublisher}" IsEnabled="{Binding $parent.DataContext.IsPublisher}" /> </TabItem> </TabControl> </Border> diff --git a/StarSim/StarSimGui/Views/Simulation.xaml b/StarSim/StarSimGui/Views/Simulation.xaml @@ -63,7 +63,7 @@ </ListBox> </Border> <Border BorderBrush="Gray" BorderThickness="2" Margin="2" Grid.Column="2" Grid.ColumnSpan="2" Grid.Row="0" Grid.RowSpan="2"> - <StackPanel Margin="2"> + <StackPanel Margin="2" IsVisible="{Binding IsPublisher}" IsEnabled="{Binding IsPublisher}"> <TextBlock HorizontalAlignment="Center">Publish Current System</TextBlock> <Separator Margin="2" /> <TextBlock HorizontalAlignment="Center">System Name</TextBlock>