Skip to content

Commit

Permalink
chore: remove WPFDialogFactory and Implement DialogService for UX enh…
Browse files Browse the repository at this point in the history
…ancements

- WPFDialogFactory has beeb renamed to DialogService. The new service methods include Create, CloseDialogs and CreateAddAppointmentsDialog.

- Created PublishNotificationEvent in the application's events to handle notifications properly.

- Added app notification support to main window and implemented the corresponding event subscriber

Signed-off-by: Russell Camo <[email protected]>
  • Loading branch information
russkyc committed Aug 29, 2023
1 parent f9f366f commit 8543652
Show file tree
Hide file tree
Showing 19 changed files with 294 additions and 108 deletions.
6 changes: 5 additions & 1 deletion GroomWise.Application/Enums/AppViews.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ namespace GroomWise.Application.Enums;
public enum AppViews
{
Login,
Main
Main,
AddAppointment,
AddCustomer,
AddPet,

}
8 changes: 8 additions & 0 deletions GroomWise.Application/Events/CreateAppointmentEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (C) 2023 Russell Camo (Russkyc).- All Rights Reserved
//
// Unauthorized copying or redistribution of all files, in source and binary forms via any medium
// without written, signed consent from the author is strictly prohibited.

namespace GroomWise.Application.Events;

public record CreateAppointmentEvent();
10 changes: 10 additions & 0 deletions GroomWise.Application/Events/PublishNotificationEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (C) 2023 Russell Camo (Russkyc).- All Rights Reserved
//
// Unauthorized copying or redistribution of all files, in source and binary forms via any medium
// without written, signed consent from the author is strictly prohibited.

using GroomWise.Domain.Enums;

namespace GroomWise.Application.Events;

public record PublishNotificationEvent(string Content, NotificationType NotificationType);
5 changes: 5 additions & 0 deletions GroomWise.Application/Mappers/AppointmentMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ public static ObservableAppointment ToObservable(this Appointment appointment)
{
return appointment.Adapt<ObservableAppointment>();
}

public static Appointment ToEntity(this ObservableAppointment observableAppointment)
{
return observableAppointment.Adapt<Appointment>();
}
}
41 changes: 29 additions & 12 deletions GroomWise.Application/ViewModels/AppViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using GroomWise.Application.Enums;
using GroomWise.Application.Events;
using GroomWise.Application.Observables;
using GroomWise.Domain.Enums;
using GroomWise.Infrastructure.Authentication.Enums;
using GroomWise.Infrastructure.Authentication.Interfaces;
Expand All @@ -16,34 +17,35 @@
using MvvmGen;
using MvvmGen.Events;
using MvvmGen.ViewModels;
using Swordfish.NET.Collections;

namespace GroomWise.Application.ViewModels;

[Inject(typeof(IAuthenticationService))]
[Inject(typeof(IDialogFactory))]
[Inject(typeof(IDialogService))]
[Inject(typeof(INavigationService))]
[Inject(typeof(IAppServicesContainer))]
[Inject(typeof(IConfigurationService))]
[Inject(typeof(IThemeManagerService))]
[Inject(typeof(IEventAggregator))]
[Inject(typeof(IConfigurationService), PropertyAccessModifier = AccessModifier.Public)]
[Inject(typeof(DashboardViewModel))]
[ViewModel]
[RegisterSingleton]
public partial class AppViewModel
public partial class AppViewModel : IEventSubscriber<PublishNotificationEvent>
{
[Property]
private IConfigurationService _configuration;

[Property]
private ViewModelBase _pageContext;

[Property]
private IList<Role> _authenticatedUserRoles;

[Property]
private ConcurrentObservableCollection<ObservableNotification> _notifications;

partial void OnInitialize()
{
Notifications = new();
PageContext = DashboardViewModel;
Configuration = ConfigurationService;
AuthenticatedUserRoles = AuthenticationService.GetSession()?.Roles!;
}

Expand All @@ -52,7 +54,7 @@ private async Task Logout()
{
await Task.Run(async () =>
{
var dialogResult = DialogFactory.Create(
var dialogResult = DialogService.Create(
"GroomWise",
"Are you sure you want to log out?",
NavigationService
Expand Down Expand Up @@ -94,9 +96,9 @@ private async Task SetDarkTheme(object param)
{
if (param is bool useDarkTheme)
{
Configuration.DarkMode = true;
OnPropertyChanged(nameof(Configuration.DarkMode));
ConfigurationService.DarkMode = true;
ThemeManagerService.SetDarkTheme(useDarkTheme);
OnPropertyChanged(nameof(ConfigurationService.DarkMode));
}
});
}
Expand All @@ -108,10 +110,25 @@ private async Task SetColorTheme(object param)
{
if (param is string themeId)
{
Configuration.ColorTheme = themeId;
OnPropertyChanged(nameof(Configuration.ColorTheme));
ConfigurationService.ColorTheme = themeId;
ThemeManagerService.SetColorTheme(themeId);
OnPropertyChanged(nameof(ConfigurationService.ColorTheme));
}
});
}

public void OnEvent(PublishNotificationEvent eventData)
{
Notifications.Add(
new ObservableNotification
{
Description = eventData.Content,
Type = eventData.NotificationType
}
);
if (Notifications.Count > 3)
{
Notifications.RemoveAt(0);
}
}
}
57 changes: 50 additions & 7 deletions GroomWise.Application/ViewModels/AppointmentViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
// Unauthorized copying or redistribution of all files, in source and binary forms via any medium
// without written, signed consent from the author is strictly prohibited.

using System.Collections.ObjectModel;
using GroomWise.Application.Events;
using GroomWise.Application.Mappers;
using GroomWise.Application.Observables;
using GroomWise.Domain.Enums;
using GroomWise.Infrastructure.Database;
using GroomWise.Infrastructure.Logging.Interfaces;
using GroomWise.Infrastructure.Navigation.Interfaces;
using GroomWise.Infrastructure.Storage.Interfaces;
using Injectio.Attributes;
using MvvmGen;
using MvvmGen.Events;
using Swordfish.NET.Collections;

namespace GroomWise.Application.ViewModels;
Expand All @@ -19,6 +22,9 @@ namespace GroomWise.Application.ViewModels;
[ViewModelGenerateInterface]
[Inject(typeof(ILogger))]
[Inject(typeof(IFileStorage))]
[Inject(typeof(IDialogService))]
[Inject(typeof(IEventAggregator))]
[Inject(typeof(INavigationService))]
[Inject(typeof(GroomWiseDbContext))]
[RegisterSingleton]
public partial class AppointmentViewModel
Expand All @@ -35,21 +41,58 @@ public partial class AppointmentViewModel
partial void OnInitialize()
{
PopulateCollections();
ActiveAppointment = new ObservableAppointment { Date = DateTime.Today };
}

private void PopulateCollections()
{
var appointments = GroomWiseDbContext!.Appointments
var appointments = GroomWiseDbContext.Appointments
.GetAll()
.Select(AppointmentMapper.ToObservable);
.Select(AppointmentMapper.ToObservable)
.OrderBy(appointment => appointment.Date);

var groomingServices = GroomWiseDbContext!.GroomingServices
var services = GroomWiseDbContext.GroomingServices
.GetAll()
.Select(GroomingServiceMapper.ToObservable);

Appointments = new ConcurrentObservableCollection<ObservableAppointment>(appointments);
GroomingServices = new ConcurrentObservableCollection<ObservableGroomingService>(
groomingServices
);
GroomingServices = new ConcurrentObservableCollection<ObservableGroomingService>(services);
}

[Command]
private async Task CreateAppointment()
{
await Task.Run(() =>
{
DialogService.CreateAddAppointmentsDialog(this, NavigationService);
});
}

[Command]
private async Task SaveAppointment()
{
await Task.Run(() =>
{
var dialogResult = DialogService.Create(
"GroomWise",
"Create Appointment?",
NavigationService
);
if (dialogResult is true)
{
var appointment = ActiveAppointment.ToEntity();
GroomWiseDbContext.Appointments.Insert(appointment);
ActiveAppointment = new ObservableAppointment { Date = DateTime.Today };
OnPropertyChanged(nameof(ActiveAppointment.Date));
PopulateCollections();
DialogService.CloseDialogs(NavigationService);
EventAggregator.Publish(
new PublishNotificationEvent(
$"Appointment {ActiveAppointment.Service} saved",
NotificationType.Success
)
);
}
});
}
}
29 changes: 28 additions & 1 deletion GroomWise.Application/ViewModels/LoginViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,35 @@ private async Task Login()
{
await Task.Run(async () =>
{
var result = AuthenticationService.Login(Username, Password);
if (string.IsNullOrEmpty(Username))
{
HasErrors = true;
Notifications.RemoveLast();
Notifications.Add(
new ObservableNotification
{
Type = NotificationType.Danger,
Description = "Username cannot be blank."
}
);
return;
}
if (string.IsNullOrEmpty(Password))
{
HasErrors = true;
Notifications.RemoveLast();
Notifications.Add(
new ObservableNotification
{
Type = NotificationType.Danger,
Description = "Password cannot be blank."
}
);
return;
}
var result = AuthenticationService.Login(Username, Password);
if (result is AuthenticationStatus.InvalidAccount)
{
HasErrors = true;
Expand Down
16 changes: 2 additions & 14 deletions GroomWise.Application/ViewModels/SettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,12 @@
// without written, signed consent from the author is strictly prohibited.

using GroomWise.Infrastructure.Configuration.Interfaces;
using GroomWise.Infrastructure.Theming.Interfaces;
using Injectio.Attributes;
using MvvmGen;

namespace GroomWise.Application.ViewModels;

[ViewModel]
[Inject(typeof(IThemeManagerService))]
[Inject(typeof(IConfigurationService))]
[Inject(typeof(IConfigurationService), PropertyAccessModifier = AccessModifier.Public)]
[RegisterSingleton]
public partial class SettingsViewModel
{
[Property]
private IConfigurationService _configuration;

partial void OnInitialize()
{
Configuration = ConfigurationService;
}

}
public partial class SettingsViewModel { }
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// Copyright (C) 2023 Russell Camo (Russkyc).- All Rights Reserved
//
//
// Unauthorized copying or redistribution of all files, in source and binary forms via any medium
// without written, signed consent from the author is strictly prohibited.

namespace GroomWise.Infrastructure.Navigation.Interfaces;

public interface IDialogFactory
public interface IDialogService
{
bool? Create(string messageBoxText, string caption, INavigationService navigationService);
}

void CloseDialogs(INavigationService navigationService);

void CreateAddAppointmentsDialog(object viewModel, INavigationService navigationService);
}
39 changes: 27 additions & 12 deletions GroomWise.WPF/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,46 @@ public App()
.AddEventAggregator()
.BuildServiceProvider();

var scope = container.GetService<IAppServicesContainer>();
scope?.AddContainer(container);

var main = scope?.GetService<MainView>();
var login = scope?.GetService<LoginView>();
var navigation = scope?.GetService<INavigationService>();
var scope = container.GetService<IAppServicesContainer>()!;
scope.AddContainer(container);
RegisterNavigationViews(scope);
LoadThemeDefaults(scope);
StartApp(scope);
}

private void RegisterNavigationViews(IAppServicesContainer scope)
{
Dispatcher.BeginInvoke(() =>
{
navigation?.Initialize(SynchronizationContext.Current!, login!);
navigation?.Add(AppViews.Login, login!);
navigation?.Add(AppViews.Main, main!);
var navigation = scope.GetService<INavigationService>()!;
var main = scope.GetService<MainView>();
var login = scope.GetService<LoginView>();
var addAppointment = scope.GetService<AddAppointmentsView>();
navigation.Initialize(SynchronizationContext.Current!, login!);
navigation.Add(AppViews.Login, login!);
navigation.Add(AppViews.Main, main!);
navigation.Add(AppViews.AddAppointment, addAppointment!);
});
}

private void LoadThemeDefaults(IAppServicesContainer scope)
{
// Load Theme Defaults
var config = scope?.GetService<IConfigurationService>();
var themeManager = scope?.GetService<IThemeManagerService>();
var config = scope.GetService<IConfigurationService>();
var themeManager = scope.GetService<IThemeManagerService>();

if (config is not null && themeManager is not null)
{
themeManager.SetDarkTheme(config.DarkMode);
themeManager.SetColorTheme(config.ColorTheme);
}
}

MainWindow = login;
private void StartApp(IAppServicesContainer scope)
{
MainWindow = scope.GetService<LoginView>();
MainWindow?.Show();
}
}
Loading

0 comments on commit 8543652

Please sign in to comment.