diff --git a/Presence.Desktop/App.axaml b/Presence.Desktop/App.axaml index 6e40e93..e74235a 100644 --- a/Presence.Desktop/App.axaml +++ b/Presence.Desktop/App.axaml @@ -1,13 +1,9 @@ - - - diff --git a/Presence.Desktop/App.axaml.cs b/Presence.Desktop/App.axaml.cs index dcc513c..20da721 100644 --- a/Presence.Desktop/App.axaml.cs +++ b/Presence.Desktop/App.axaml.cs @@ -26,18 +26,18 @@ namespace Presence.Desktop var services = serviceCollection.BuildServiceProvider(); - var mainViewModel = services.GetRequiredService(); + var mainViewModel = services.GetRequiredService(); + - var groupUseCase = services.GetRequiredService(); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - - desktop.MainWindow = new MainWindow(groupUseCase) + + desktop.MainWindow = new MainWindow() { - DataContext = mainViewModel, + DataContext = new MainWindowViewModel(services), }; } diff --git a/Presence.Desktop/DI/ServiceCollectionExtensions.cs b/Presence.Desktop/DI/ServiceCollectionExtensions.cs index de132d4..12a21ac 100644 --- a/Presence.Desktop/DI/ServiceCollectionExtensions.cs +++ b/Presence.Desktop/DI/ServiceCollectionExtensions.cs @@ -18,7 +18,7 @@ namespace Presence.Desktop.DI .AddSingleton() .AddSingleton() .AddTransient() - .AddTransient(); + .AddTransient(); } } } \ No newline at end of file diff --git a/Presence.Desktop/Presence.Desktop.csproj b/Presence.Desktop/Presence.Desktop.csproj index 92a908c..337988a 100644 --- a/Presence.Desktop/Presence.Desktop.csproj +++ b/Presence.Desktop/Presence.Desktop.csproj @@ -31,4 +31,10 @@ + + + + GroupView.axaml + + diff --git a/Presence.Desktop/ViewLocator.cs b/Presence.Desktop/ViewLocator.cs index 74b8a57..2da0eb6 100644 --- a/Presence.Desktop/ViewLocator.cs +++ b/Presence.Desktop/ViewLocator.cs @@ -2,31 +2,18 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using Presence.Desktop.ViewModels; using System; +using Presence.Desktop.Views; +using ReactiveUI; namespace Presence.Desktop { - public class ViewLocator : IDataTemplate + public class ViewLocator : IViewLocator { - - public Control? Build(object? param) + public IViewFor? ResolveView(T? viewModel, string? contract = null) => viewModel switch { - if (param is null) - return null; - - var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal); - var type = Type.GetType(name); - - if (type != null) - { - return (Control)Activator.CreateInstance(type)!; - } - - return new TextBlock { Text = "Not Found: " + name }; - } - - public bool Match(object? data) - { - return data is ViewModelBase; - } + GroupViewModel groupViewModel => new GroupView { DataContext = groupViewModel }, + PresenceViewModel presenceViewModel => new PresenceView { DataContext = presenceViewModel }, + _ => throw new ArgumentOutOfRangeException(nameof(viewModel)) + }; } } diff --git a/Presence.Desktop/ViewModels/GroupViewModel.cs b/Presence.Desktop/ViewModels/GroupViewModel.cs new file mode 100644 index 0000000..e7c746d --- /dev/null +++ b/Presence.Desktop/ViewModels/GroupViewModel.cs @@ -0,0 +1,239 @@ +using Avalonia.Controls.ApplicationLifetimes; +using domain.Models; +using domain.UseCase; +using Presence.Desktop.Views; +using ReactiveUI; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Windows.Input; +using Avalonia; + +namespace Presence.Desktop.ViewModels +{ + public class GroupViewModel : ViewModelBase, IRoutableViewModel + { + private readonly GroupUseCase _groupUseCase; + private List groupPresentersDataSource = new List(); + private ObservableCollection _groups; + public ObservableCollection Groups => _groups; + + private GroupPresenter? _selectedGroupItem; + public GroupPresenter? SelectedGroupItem + { + get => _selectedGroupItem; + set => this.RaiseAndSetIfChanged(ref _selectedGroupItem, value); + } + + public ObservableCollection Users { get => _users; } + private ObservableCollection _users; + + // Список опций сортировки + public List SortOptions { get; } = new List { "По фамилии", "По убыванию" }; + + private string _selectedSortOption; + public string SelectedSortOption + { + get => _selectedSortOption; + set => this.RaiseAndSetIfChanged(ref _selectedSortOption, value); + } + + public bool CanDelete => SelectedUsers?.Count > 0; + public bool CanEdit => SelectedUsers?.Count == 1; + + public ObservableCollection SelectedUsers { get; set; } = new ObservableCollection(); + + public ReactiveCommand OnDeleteUserClicks { get; } + public ReactiveCommand EditUserCommand { get; } + public ICommand RemoveAllStudentsCommand { get; } + public ICommand AddStudentCommand { get; } + + public GroupViewModel(GroupUseCase groupUseCase) + { + _groupUseCase = groupUseCase; + + OnDeleteUserClicks = ReactiveCommand.Create(OnDeleteUserClick, this.WhenAnyValue(vm => vm.CanDelete)); + EditUserCommand = ReactiveCommand.Create(OnEditUserClick, this.WhenAnyValue(vm => vm.CanEdit)); + + RefreshGroups(); + + _groups = new ObservableCollection(groupPresentersDataSource); + _users = new ObservableCollection(); + + this.WhenAnyValue(vm => vm.SelectedGroupItem) + .Subscribe(vm => SetUsers()); + + this.WhenAnyValue(vm => vm.SelectedSortOption) + .Subscribe(_ => SortUsers()); + + RemoveAllStudentsCommand = ReactiveCommand.Create(RemoveAllStudents); + AddStudentCommand = ReactiveCommand.Create(AddStudent); + + SelectedUsers.CollectionChanged += (s, e) => + { + this.RaisePropertyChanged(nameof(CanDelete)); + this.RaisePropertyChanged(nameof(CanEdit)); + }; + } + + private void SetUsers() + { + if (SelectedGroupItem?.users == null) return; + + Users.Clear(); + foreach (var item in SelectedGroupItem.users) + { + Users.Add(item); + } + + SortUsers(); + } + + private void SortUsers() + { + if (SelectedGroupItem?.users == null) return; + + var sortedUsers = SelectedGroupItem.users.ToList(); + + switch (SelectedSortOption) + { + case "По фамилии": + sortedUsers = sortedUsers.OrderBy(u => u.Name).ToList(); + break; + case "По убыванию": + sortedUsers = sortedUsers.OrderByDescending(u => u.Name).ToList(); + break; + } + + Users.Clear(); + foreach (var item in sortedUsers) + { + Users.Add(item); + } + } + + private void RemoveAllStudents() + { + if (SelectedGroupItem == null) return; + + _groupUseCase.RemoveAllStudentsFromGroup(SelectedGroupItem.Id); + SelectedGroupItem.users = new List(); + SetUsers(); + } + + private void AddStudent() + { + if (SelectedGroupItem == null) return; + + var newStudent = new UserPresenter + { + Name = "Новый студент", + Guid = Guid.NewGuid(), + Group = SelectedGroupItem + }; + + _groupUseCase.AddStudentToGroup(SelectedGroupItem.Id, new User + { + Guid = newStudent.Guid, + FIO = newStudent.Name + }); + + var updatedUsers = SelectedGroupItem.users?.ToList() ?? new List(); + updatedUsers.Add(newStudent); + SelectedGroupItem.users = updatedUsers; + SetUsers(); + } + + public void OnDeleteUserClick() + { + if (SelectedUsers.Count == 0 || SelectedGroupItem?.users == null) + return; + + foreach (var user in SelectedUsers.ToList()) + { + _groupUseCase.RemoveUserByGuid(user.Guid); + + var updatedUsers = SelectedGroupItem.users.Where(u => u.Guid != user.Guid).ToList(); + SelectedGroupItem.users = new List(updatedUsers); + } + + SetUsers(); + SelectedUsers.Clear(); + this.RaisePropertyChanged(nameof(CanDelete)); + this.RaisePropertyChanged(nameof(CanEdit)); + } + + public async void OnEditUserClick() + { + var user = SelectedUsers.FirstOrDefault(); + if (user == null) return; + + var groups = _groupUseCase.GetAllGroups(); + + // Преобразуем группы из domain.Models.Group в GroupPresenter + var groupPresenters = groups.Select(g => new GroupPresenter + { + Id = g.Id, + Name = g.Name, + users = g.Users?.Select(u => new UserPresenter + { + Name = u.FIO, + Guid = u.Guid, + Group = new GroupPresenter { Id = g.Id, Name = g.Name } + }).ToList() + }).ToList(); + + var editDialog = new EditUserDialog(user.Guid, user.Name, user.Group.Id, groupPresenters); + + var mainWindow = (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow; + if (mainWindow == null) return; + + var result = await editDialog.ShowDialog(mainWindow); + + if (result != (null, null)) + { + var newName = result.Item1; + var newGroup = result.Item2; + + user.Name = newName; + user.Group = newGroup; + + _groupUseCase.UpdateUser(user.Guid, user.Name, user.Group.Id); + + SetUsers(); + SelectedUsers.Clear(); + this.RaisePropertyChanged(nameof(CanEdit)); + this.RaisePropertyChanged(nameof(CanDelete)); + } + RefreshGroups(); + } + + private void RefreshGroups() + { + groupPresentersDataSource.Clear(); + foreach (var item in _groupUseCase.GetAllGroups()) + { + GroupPresenter groupPresenter = new GroupPresenter + { + Id = item.Id, + Name = item.Name, + users = item.Users?.Select(user => new UserPresenter + { + Name = user.FIO, + Guid = user.Guid, + Group = new GroupPresenter { Id = item.Id, Name = item.Name } + }).ToList() + }; + groupPresentersDataSource.Add(groupPresenter); + } + _groups = new ObservableCollection(groupPresentersDataSource); + } + public string? UrlPathSegment { get; } + public IScreen HostScreen { get; } + + + } +} \ No newline at end of file diff --git a/Presence.Desktop/ViewModels/MainWindowViewModel.cs b/Presence.Desktop/ViewModels/MainWindowViewModel.cs index 79ca60c..8449f0d 100644 --- a/Presence.Desktop/ViewModels/MainWindowViewModel.cs +++ b/Presence.Desktop/ViewModels/MainWindowViewModel.cs @@ -1,235 +1,16 @@ -using Avalonia.Controls.ApplicationLifetimes; -using domain.Models; -using domain.UseCase; -using Presence.Desktop.Views; +using System; +using Microsoft.Extensions.DependencyInjection; using ReactiveUI; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reactive; -using System.Reactive.Linq; -using System.Windows.Input; -using Avalonia; -namespace Presence.Desktop.ViewModels +namespace Presence.Desktop.ViewModels; + +public class MainWindowViewModel : ViewModelBase, IScreen { - public class MainWindowViewModel : ViewModelBase + public RoutingState Router { get; } = new RoutingState(); + + public MainWindowViewModel(IServiceProvider serviceProvider) { - private readonly GroupUseCase _groupUseCase; - private List groupPresentersDataSource = new List(); - private ObservableCollection _groups; - public ObservableCollection Groups => _groups; - - private GroupPresenter? _selectedGroupItem; - public GroupPresenter? SelectedGroupItem - { - get => _selectedGroupItem; - set => this.RaiseAndSetIfChanged(ref _selectedGroupItem, value); - } - - public ObservableCollection Users { get => _users; } - private ObservableCollection _users; - - // Список опций сортировки - public List SortOptions { get; } = new List { "По фамилии", "По убыванию" }; - - private string _selectedSortOption; - public string SelectedSortOption - { - get => _selectedSortOption; - set => this.RaiseAndSetIfChanged(ref _selectedSortOption, value); - } - - public bool CanDelete => SelectedUsers?.Count > 0; - public bool CanEdit => SelectedUsers?.Count == 1; - - public ObservableCollection SelectedUsers { get; set; } = new ObservableCollection(); - - public ReactiveCommand OnDeleteUserClicks { get; } - public ReactiveCommand EditUserCommand { get; } - public ICommand RemoveAllStudentsCommand { get; } - public ICommand AddStudentCommand { get; } - - public MainWindowViewModel(GroupUseCase groupUseCase) - { - _groupUseCase = groupUseCase; - - OnDeleteUserClicks = ReactiveCommand.Create(OnDeleteUserClick, this.WhenAnyValue(vm => vm.CanDelete)); - EditUserCommand = ReactiveCommand.Create(OnEditUserClick, this.WhenAnyValue(vm => vm.CanEdit)); - - RefreshGroups(); - - _groups = new ObservableCollection(groupPresentersDataSource); - _users = new ObservableCollection(); - - this.WhenAnyValue(vm => vm.SelectedGroupItem) - .Subscribe(vm => SetUsers()); - - this.WhenAnyValue(vm => vm.SelectedSortOption) - .Subscribe(_ => SortUsers()); - - RemoveAllStudentsCommand = ReactiveCommand.Create(RemoveAllStudents); - AddStudentCommand = ReactiveCommand.Create(AddStudent); - - SelectedUsers.CollectionChanged += (s, e) => - { - this.RaisePropertyChanged(nameof(CanDelete)); - this.RaisePropertyChanged(nameof(CanEdit)); - }; - } - - private void SetUsers() - { - if (SelectedGroupItem?.users == null) return; - - Users.Clear(); - foreach (var item in SelectedGroupItem.users) - { - Users.Add(item); - } - - SortUsers(); - } - - private void SortUsers() - { - if (SelectedGroupItem?.users == null) return; - - var sortedUsers = SelectedGroupItem.users.ToList(); - - switch (SelectedSortOption) - { - case "По фамилии": - sortedUsers = sortedUsers.OrderBy(u => u.Name).ToList(); - break; - case "По убыванию": - sortedUsers = sortedUsers.OrderByDescending(u => u.Name).ToList(); - break; - } - - Users.Clear(); - foreach (var item in sortedUsers) - { - Users.Add(item); - } - } - - private void RemoveAllStudents() - { - if (SelectedGroupItem == null) return; - - _groupUseCase.RemoveAllStudentsFromGroup(SelectedGroupItem.Id); - SelectedGroupItem.users = new List(); - SetUsers(); - } - - private void AddStudent() - { - if (SelectedGroupItem == null) return; - - var newStudent = new UserPresenter - { - Name = "Новый студент", - Guid = Guid.NewGuid(), - Group = SelectedGroupItem - }; - - _groupUseCase.AddStudentToGroup(SelectedGroupItem.Id, new User - { - Guid = newStudent.Guid, - FIO = newStudent.Name - }); - - var updatedUsers = SelectedGroupItem.users?.ToList() ?? new List(); - updatedUsers.Add(newStudent); - SelectedGroupItem.users = updatedUsers; - SetUsers(); - } - - public void OnDeleteUserClick() - { - if (SelectedUsers.Count == 0 || SelectedGroupItem?.users == null) - return; - - foreach (var user in SelectedUsers.ToList()) - { - _groupUseCase.RemoveUserByGuid(user.Guid); - - var updatedUsers = SelectedGroupItem.users.Where(u => u.Guid != user.Guid).ToList(); - SelectedGroupItem.users = new List(updatedUsers); - } - - SetUsers(); - SelectedUsers.Clear(); - this.RaisePropertyChanged(nameof(CanDelete)); - this.RaisePropertyChanged(nameof(CanEdit)); - } - - public async void OnEditUserClick() - { - var user = SelectedUsers.FirstOrDefault(); - if (user == null) return; - - var groups = _groupUseCase.GetAllGroups(); - - // Преобразуем группы из domain.Models.Group в GroupPresenter - var groupPresenters = groups.Select(g => new GroupPresenter - { - Id = g.Id, - Name = g.Name, - users = g.Users?.Select(u => new UserPresenter - { - Name = u.FIO, - Guid = u.Guid, - Group = new GroupPresenter { Id = g.Id, Name = g.Name } - }).ToList() - }).ToList(); - - var editDialog = new EditUserDialog(user.Guid, user.Name, user.Group.Id, groupPresenters); - - var mainWindow = (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow; - if (mainWindow == null) return; - - var result = await editDialog.ShowDialog(mainWindow); - - if (result != (null, null)) - { - var newName = result.Item1; - var newGroup = result.Item2; - - user.Name = newName; - user.Group = newGroup; - - _groupUseCase.UpdateUser(user.Guid, user.Name, user.Group.Id); - - SetUsers(); - SelectedUsers.Clear(); - this.RaisePropertyChanged(nameof(CanEdit)); - this.RaisePropertyChanged(nameof(CanDelete)); - } - RefreshGroups(); - } - - private void RefreshGroups() - { - groupPresentersDataSource.Clear(); - foreach (var item in _groupUseCase.GetAllGroups()) - { - GroupPresenter groupPresenter = new GroupPresenter - { - Id = item.Id, - Name = item.Name, - users = item.Users?.Select(user => new UserPresenter - { - Name = user.FIO, - Guid = user.Guid, - Group = new GroupPresenter { Id = item.Id, Name = item.Name } - }).ToList() - }; - groupPresentersDataSource.Add(groupPresenter); - } - _groups = new ObservableCollection(groupPresentersDataSource); - } + var groupViewModel = serviceProvider.GetRequiredService(); + Router.Navigate.Execute(groupViewModel); } } \ No newline at end of file diff --git a/Presence.Desktop/ViewModels/PresenceViewModel.cs b/Presence.Desktop/ViewModels/PresenceViewModel.cs new file mode 100644 index 0000000..4c06266 --- /dev/null +++ b/Presence.Desktop/ViewModels/PresenceViewModel.cs @@ -0,0 +1,9 @@ +using ReactiveUI; + +namespace Presence.Desktop.ViewModels; + +public class PresenceViewModel : ViewModelBase, IRoutableViewModel +{ + public string? UrlPathSegment { get; } + public IScreen HostScreen { get; } +} diff --git a/Presence.Desktop/Views/GroupView.axaml b/Presence.Desktop/Views/GroupView.axaml new file mode 100644 index 0000000..854428d --- /dev/null +++ b/Presence.Desktop/Views/GroupView.axaml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +