using CommunityToolkit.Mvvm.ComponentModel; using Explorer.Domain; namespace Explorer.Presentation; public sealed partial class FolderItemViewModel : ObservableObject { [ObservableProperty] private bool _isSelected; [ObservableProperty] private bool _isRenaming; [ObservableProperty] private bool _isDropTarget; [ObservableProperty] private string _editName = ""; public FolderItemViewModel(FileSystemItem item, bool sizeFromIndex) { Item = item; SizeFromIndex = sizeFromIndex; _editName = item.Name; } public void BeginRename() { EditName = Item.Name; IsRenaming = true; } public void CancelRename() { IsRenaming = false; EditName = Item.Name; } public FileSystemItem Item { get; private set; } public bool SizeFromIndex { get; private set; } public void Apply(FileSystemItem item, bool? sizeFromIndex = null) { Item = item; if (sizeFromIndex is bool value) { SizeFromIndex = value; } OnPropertyChanged(string.Empty); } public string Name => Item.DisplayName ?? Item.Name; public string FullPath => Item.FullPath; public bool IsDirectory => Item.IsDirectory; public string TypeLabel => Item.AvailableToImport ? "Available in Windows" : Item.Location.IsRecycleBin ? "Recycle Bin" : Item.IsDirectory ? "File folder" : (Item.ExtensionDisplay()); public string SizeLabel => Item.SizeKnowledge switch { SizeKnowledge.Unknown when Item.Location.AccessDenied => "Access denied", SizeKnowledge.Unknown => "", SizeKnowledge.Partial when Item.SizeBytes > 0 => $"{Formatters.Size(Item.SizeBytes)} (partial)", SizeKnowledge.Partial => "Access denied", _ when Item.IsDirectory && !SizeFromIndex && Item.SizeBytes == 0 => "", _ => Formatters.Size(Item.SizeBytes) }; public string FreeSpaceLabel => Item.FreeSpaceBytes is long free ? Formatters.Size(free) : ""; public string FreeSpaceTooltip => Item.FreeSpaceBytes is long free && Item.CapacityBytes is long total && total > 0 ? $"{Formatters.Size(free)} free of {Formatters.Size(total)}" : FreeSpaceLabel; public string StatusGlyph => Item.Location.IsRecycleBin ? "🗑" : Item.Location.IsProtected || Item.Location.AccessDenied ? "⚠" : ""; public bool HasStatusGlyph => StatusGlyph.Length > 0; public string ModifiedLabel => Formatters.Date(Item.ModifiedUtc); public string CreatedLabel => Formatters.Date(Item.CreatedUtc); public string IconGlyph => Item.IsDirectory ? "\uE8B7" : "\uE8A5"; public bool IsImage => !Item.IsDirectory && MediaKinds.IsImage(Item.Name); public bool IsVideo => !Item.IsDirectory && MediaKinds.IsVideo(Item.Name); public bool MayHydrateOnRead => Item.Cloud?.MayHydrateOnRead == true || AttributeFlags.MayHydrateOnRead(Item.Attributes); public string CloudStatus => Item.Cloud?.StatusText ?? ""; public string CloudLabel => Item.Cloud?.Availability switch { CloudAvailability.OnlineOnly => "Online-only", CloudAvailability.LocallyAvailable => "Local", CloudAvailability.Pinned => "Pinned", CloudAvailability.Syncing => "Syncing", CloudAvailability.Error => "Error", _ => "" }; public bool HasCloudStatus => !string.IsNullOrEmpty(CloudLabel); [ObservableProperty] private string _gitLabel = ""; public bool HasGitLabel => !string.IsNullOrEmpty(GitLabel); [ObservableProperty] private object? _thumbnail; public bool HasThumbnail => Thumbnail is not null; public void SetGit(string? label) => GitLabel = label ?? ""; public void SetThumbnail(object? image) => Thumbnail = image; partial void OnGitLabelChanged(string value) => OnPropertyChanged(nameof(HasGitLabel)); partial void OnThumbnailChanged(object? value) => OnPropertyChanged(nameof(HasThumbnail)); public string SizeTooltip { get { var logical = SizeLabel; var allocated = Item.AllocatedSizeBytes ?? Item.Cloud?.AllocatedSizeBytes; if (allocated is long disk && disk != Item.SizeBytes && !Item.IsDirectory) { return string.IsNullOrEmpty(CloudStatus) ? $"Size {logical} · On disk {Formatters.Size(disk)}" : $"{CloudStatus} · Size {logical} · On disk {Formatters.Size(disk)}"; } if (Item.Location.AccessDenied) { return string.IsNullOrEmpty(CloudStatus) ? "Access denied" : $"{CloudStatus} · Access denied"; } return string.IsNullOrEmpty(CloudStatus) ? logical : $"{CloudStatus} · {logical}"; } } } internal static class MediaKinds { private static readonly HashSet Images = new(StringComparer.OrdinalIgnoreCase) { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".tif", ".tiff", ".ico", ".jfif" }; private static readonly HashSet Videos = new(StringComparer.OrdinalIgnoreCase) { ".mp4", ".mkv", ".avi", ".mov", ".wmv", ".webm", ".m4v", ".mpg", ".mpeg" }; public static bool IsImage(string name) => Images.Contains(Path.GetExtension(name)); public static bool IsVideo(string name) => Videos.Contains(Path.GetExtension(name)); } file static class ItemExt { public static string ExtensionDisplay(this FileSystemItem item) { var ext = NameNormalizer.Extension(item.Name); return string.IsNullOrEmpty(ext) ? "File" : ext.ToUpperInvariant() + " file"; } }