using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Models.TreeDataGrid; using DynamicData; using DynamicData.Binding; using Flawless.Client.Models; using Flawless.Client.Service; using ReactiveUI; using ReactiveUI.SourceGenerators; using ChangeType = Flawless.Client.Service.LocalFileTreeAccessor.ChangeType; namespace Flawless.Client.ViewModels; public class LocalChangesNode { public required string FullPath { get; set; } public required string Type { get; set; } public DateTime? ModifiedTime { get; set; } public bool Included { get { if (Contents != null) return Contents.All(c => c.Included); return _actualIncluded; } set { if (Contents != null) foreach (var n in Contents) n.Included = value; _actualIncluded = value; } } private bool _actualIncluded; public ObservableCollection? Contents { get; set; } public static LocalChangesNode FromFolder(string folderPath) { return new LocalChangesNode { Type = "Folder", FullPath = folderPath, Contents = new() }; } public static LocalChangesNode FromWorkspaceFile(LocalFileTreeAccessor.ChangeRecord file) { return new LocalChangesNode { Type = file.Type.ToString(), FullPath = file.File.WorkPath, ModifiedTime = file.File.ModifyTime }; } } public partial class RepositoryViewModel : RoutableViewModelBase { public RepositoryModel Repository { get; } public RepositoryLocalDatabaseModel LocalDatabase { get; } public HierarchicalTreeDataGridSource LocalChange { get; } public ObservableCollection LocalChangeSetRaw { get; } = new(); public UserModel User { get; } [Reactive] private bool _autoDetectChanges = true; [Reactive] private bool _isOwnerRole, _isDeveloperRole, _isReporterRole, _isGuestRole; public RepositoryViewModel(RepositoryModel repo, IScreen hostScreen) : base(hostScreen) { Repository = repo; LocalDatabase = RepositoryService.C.GetRepositoryLocalDatabase(repo); User = UserService.C.GetUserInfoAsync(Api.C.Username.Value!)!; // Setup repository permission change watcher RefreshRepositoryRoleInfo(); Repository.Members.ObserveCollectionChanges().Subscribe(_ => RefreshRepositoryRoleInfo()); // Setup local change set LocalChangeSetRaw.Add(new LocalChangesNode { Type = "Add", FullPath = "test.md", ModifiedTime = DateTime.Now, }); LocalChange = new HierarchicalTreeDataGridSource(LocalChangeSetRaw) { Columns = { new CheckBoxColumn( string.Empty, n => n.Included, (n, v) => n.Included = v), new TextColumn( "Change", n => n.Contents != null ? String.Empty : n.Type), new HierarchicalExpanderColumn( new TextColumn( "Name", n => Path.GetFileName(n.FullPath)), n => n.Contents), new TextColumn( "File Type", n => n.Contents != null ? "Folder" : Path.GetExtension(n.FullPath)), new TextColumn( "Size", n => 0), new TextColumn( "ModifiedTime", n => n.ModifiedTime.HasValue ? n.ModifiedTime.Value.ToLocalTime() : null), } }; // Do refresh when entered // DetectLocalChangesAsyncCommand.Execute(); } [ReactiveCommand] private async Task CloseRepositoryAsync() { await RepositoryService.C.CloseRepositoryAsync(Repository); await HostScreen.Router.NavigateBack.Execute(); } [ReactiveCommand] private void RefreshRepositoryRoleInfo() { var isOwner = Repository.OwnerName == User.Username; var role = isOwner ? RepositoryModel.RepositoryRole.Owner : Repository.Members.First(p => p.Username == User.Username).Role; if (role >= RepositoryModel.RepositoryRole.Owner) IsOwnerRole = true; if (role >= RepositoryModel.RepositoryRole.Developer) IsDeveloperRole = true; if (role >= RepositoryModel.RepositoryRole.Reporter) IsReporterRole = true; if (role >= RepositoryModel.RepositoryRole.Guest) IsGuestRole = true; } [ReactiveCommand] private async ValueTask DetectLocalChangesAsync() { var ns = await Task.Run(() => { LocalDatabase.LocalAccessor.Refresh(); // Generate a map of all folders var folderMap = new Dictionary(); foreach (var k in LocalDatabase.LocalAccessor.Changes.Keys) AddParentToMap(k); var nodes = new List(); foreach (var file in LocalDatabase.LocalAccessor.Changes.Values) { var directory = Path.GetDirectoryName(file.File.WorkPath); var n = LocalChangesNode.FromWorkspaceFile(file); if (string.IsNullOrEmpty(directory)) nodes.Add(n); else folderMap[directory].Contents!.Add(n); } nodes.AddRange(folderMap.Values); return nodes; void AddParentToMap(string path) { var parent = Path.GetDirectoryName(path); if (string.IsNullOrEmpty(parent) || folderMap.ContainsKey(parent)) return; folderMap.Add(parent, LocalChangesNode.FromFolder(parent)); } }); LocalChangeSetRaw.Clear(); LocalChangeSetRaw.AddRange(ns); } }