using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using CORP_MAUI.Models; namespace CORP_MAUI.ViewModels; public class PostViewModel : INotifyPropertyChanged { public Post Post { get; } public PostViewModel(Post post) { Post = post; // Truncate content for preview if (!string.IsNullOrEmpty(Post.Content) && Post.Content.Length > 0) { if (Post.Content.Length > 100) { _previewContent = Post.Content.Substring(0, 100) + "..."; } else { _previewContent = Post.Content; } } else { _previewContent = string.Empty; } } private string? _localImagePath; public string? LocalImagePath { get => _localImagePath; set { _localImagePath = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsImageVisible)); OnPropertyChanged(nameof(DisplayImageSource)); } } private ImageSource? _imageSource; public ImageSource? AuthorizedImageSource { get => _imageSource; set { _imageSource = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsImageVisible)); OnPropertyChanged(nameof(DisplayImageSource)); } } public ImageSource? DisplayImageSource { get { // 1. Проверяем установленный _imageSource (для предварительно загруженных) if (_imageSource != null) return _imageSource; // 2. Проверяем, есть ли Base64 изображение в Post.Image if (Post != null && !string.IsNullOrEmpty(Post.Image)) { // Только если это Base64 - декодируем напрямую if (Post.Image.StartsWith("data:image")) { try { // Извлекаем Base64 данные из строки "data:image/jpeg;base64,..." string base64Data = Post.Image; int commaIndex = base64Data.IndexOf(','); if (commaIndex > 0) { base64Data = base64Data.Substring(commaIndex + 1); byte[] imageBytes = Convert.FromBase64String(base64Data); Trace.WriteLine($"[PostViewModel] ✅ Base64 изображение декодировано ({imageBytes.Length} bytes)"); return ImageSource.FromStream(() => new MemoryStream(imageBytes)); } } catch (Exception ex) { Trace.WriteLine($"[PostViewModel] ⚠️ Ошибка декодирования Base64 изображения: {ex.Message}"); } } // Если Post.Image содержит URL (не Base64), то он будет обработан через DisplayImageUrl // и загружен в PostsPage, после чего попадёт в _localImagePath } // 3. Проверяем локальный кеш (для URL изображений, загруженных в PostsPage) if (!string.IsNullOrEmpty(_localImagePath) && File.Exists(_localImagePath)) { // Basic check for empty files try { FileInfo fileInfo = new FileInfo(_localImagePath); if (fileInfo.Length == 0) { Trace.WriteLine($"[PostViewModel] ⚠️ Файл изображения пуст: {_localImagePath}"); return null; } } catch (Exception ex) { Trace.WriteLine($"[PostViewModel] ⚠️ Ошибка проверки файла: {ex.Message}"); return null; } // Используем FromStream для надежности, чтобы не блокировать файл // и гарантировать чтение даже из системных папок return ImageSource.FromStream(() => { try { return new FileStream(_localImagePath, FileMode.Open, FileAccess.Read, FileShare.Read); } catch (Exception ex) { Trace.WriteLine($"[PostViewModel] ⚠️ Ошибка чтения изображения {_localImagePath}: {ex.Message}"); return null; } }); } return null; } } public bool IsImageVisible => DisplayImageSource != null; private string _previewContent; public string PreviewContent { get => _previewContent; set { _previewContent = value; OnPropertyChanged(); } } // Proxy properties for cleaner XAML binding if needed, or bind to Post.Title directly public int Id => Post.Id; public string Title => Post.Title; public string? AuthorizedImageUrl => Post.DisplayImageUrl; public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }