294 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text.Json;
using System.Text.Json.Serialization;
using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls;
namespace CORP_MAUI.Models;
public class User : INotifyPropertyChanged
{
public int Id { get; set; }
public string Username { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
[JsonPropertyName("phone_number")]
public string? PhoneNumber { get; set; }
[JsonPropertyName("first_name")]
public string FirstName { get; set; } = string.Empty;
[JsonPropertyName("last_name")]
public string LastName { get; set; } = string.Empty;
[JsonPropertyName("departments")]
public List<Department>? Departments { get; set; }
// Может быть либо объектом Department, либо строкой (для обратной совместимости)
public object? Department { get; set; }
// Может быть либо объектом Position, либо строкой (для обратной совместимости)
public object? Position { get; set; }
public List<Skill>? Skills { get; set; }
[JsonPropertyName("is_online")]
public bool IsOnline { get; set; }
[JsonPropertyName("avatar_url")]
public string? AvatarUrl { get; set; }
[JsonPropertyName("avatar")]
public string? Avatar { get; set; }
// Backwards-compatible property: some code expects PhotoUrl
[JsonIgnore]
public string? PhotoUrl
{
get => AvatarUrl ?? Avatar;
set
{
if (value == AvatarUrl) return;
AvatarUrl = value;
OnPropertyChanged(nameof(AvatarUrl));
OnPropertyChanged(nameof(DisplayAvatarUrl));
OnPropertyChanged(nameof(DisplayAvatarSource));
}
}
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// Вспомогательные свойства для удобства
[JsonIgnore]
public string FullName => $"{FirstName} {LastName}".Trim();
/// <summary>
/// Проверяет, является ли аватар Base64 строкой
/// </summary>
[JsonIgnore]
public bool IsAvatarBase64 => !string.IsNullOrEmpty(Avatar) && Avatar.StartsWith("data:image");
/// <summary>
/// Получить байты изображения из Base64 строки
/// </summary>
public byte[]? GetAvatarBytesFromBase64()
{
if (!IsAvatarBase64) return null;
try
{
// Формат: data:image/jpeg;base64,<base64_data>
int commaIndex = Avatar!.IndexOf(',');
if (commaIndex < 0) return null;
string base64Data = Avatar.Substring(commaIndex + 1);
return Convert.FromBase64String(base64Data);
}
catch (Exception ex)
{
Trace.WriteLine($"[User] Ошибка декодирования Base64 аватара: {ex.Message}");
return null;
}
}
[JsonIgnore]
public string? DisplayAvatarUrl
{
get
{
// Если явно указана Avatar (base64 or url)
if (!string.IsNullOrEmpty(Avatar))
{
if (Avatar.StartsWith("data:image"))
return null;
if (Avatar.StartsWith("http", StringComparison.OrdinalIgnoreCase))
return Avatar;
if (!Avatar.StartsWith("data:"))
{
string cleanUrl = Avatar.TrimStart('/');
return ServiceProvider.ApiService.GetFullImageUrl(cleanUrl);
}
}
// Используем AvatarUrl (обычно относительный или абсолютный)
if (!string.IsNullOrEmpty(AvatarUrl))
{
return ServiceProvider.ApiService.GetFullImageUrl(AvatarUrl);
}
return null;
}
}
/// <summary>
/// Кэшированный ImageSource аватара (не создаётся каждый раз)
/// </summary>
[JsonIgnore]
private ImageSource? _cachedImageSource;
/// <summary>
/// Путь к файлу кэшированного аватара
/// </summary>
[JsonIgnore]
private string? _cachedAvatarFilePath;
[JsonIgnore]
public string? CachedAvatarFilePath
{
get => _cachedAvatarFilePath;
set
{
if (_cachedAvatarFilePath == value) return;
_cachedAvatarFilePath = value;
OnPropertyChanged();
OnPropertyChanged(nameof(DisplayAvatarSource));
}
}
/// <summary>
/// Флаг, что аватар был успешно загружен
/// </summary>
[JsonIgnore]
public bool IsAvatarLoaded { get; private set; }
/// <summary>
/// Установить путь к файлу кэшированного аватара
/// </summary>
public void SetCachedAvatarFilePath(string filePath)
{
Trace.WriteLine($"[User.SetCachedAvatarFilePath] {FullName}: Setting path to {filePath}");
CachedAvatarFilePath = filePath;
_cachedImageSource = ImageSource.FromFile(filePath);
IsAvatarLoaded = true;
Trace.WriteLine($"[User.SetCachedAvatarFilePath] {FullName}: ImageSource created, notifying UI...");
// Уведомляем UI об изменении аватара
MainThread.BeginInvokeOnMainThread(() =>
{
Trace.WriteLine($"[User.SetCachedAvatarFilePath] {FullName}: OnPropertyChanged(DisplayAvatarSource)");
OnPropertyChanged(nameof(DisplayAvatarSource));
});
}
/// <summary>
/// Сбросить кэш ImageSource
/// </summary>
public void ResetImageSourceCache()
{
_cachedImageSource = null;
IsAvatarLoaded = false;
CachedAvatarFilePath = null;
OnPropertyChanged(nameof(DisplayAvatarSource));
}
/// <summary>
/// Универсальное свойство для отображения аватара
/// </summary>
[JsonIgnore]
public ImageSource? DisplayAvatarSource
{
get
{
Trace.WriteLine($"[User.DisplayAvatarSource] {FullName}: IsAvatarLoaded={IsAvatarLoaded}, CachedPath={CachedAvatarFilePath}");
// Если аватар уже загружен - возвращаем кэшированный
if (IsAvatarLoaded && _cachedImageSource != null)
{
Trace.WriteLine($"[User.DisplayAvatarSource] {FullName}: Returning cached ImageSource");
return _cachedImageSource;
}
// Проверяем путь к файлу
if (!string.IsNullOrEmpty(CachedAvatarFilePath) && File.Exists(CachedAvatarFilePath))
{
Trace.WriteLine($"[User.DisplayAvatarSource] {FullName}: Loading from file: {CachedAvatarFilePath}");
_cachedImageSource = ImageSource.FromFile(CachedAvatarFilePath);
IsAvatarLoaded = true;
return _cachedImageSource;
}
try
{
// Проверяем глобальный файловый кэш
string? url = DisplayAvatarUrl;
if (!string.IsNullOrEmpty(url))
{
string? cachedPath = Services.ImageCacheService.Instance.GetCachedFilePath(url);
if (!string.IsNullOrEmpty(cachedPath) && File.Exists(cachedPath))
{
Trace.WriteLine($"[User.DisplayAvatarSource] {FullName}: Found in global cache: {cachedPath}");
CachedAvatarFilePath = cachedPath;
_cachedImageSource = ImageSource.FromFile(cachedPath);
IsAvatarLoaded = true;
return _cachedImageSource;
}
// Если нет в кэше - возвращаем placeholder
Trace.WriteLine($"[User.DisplayAvatarSource] {FullName}: Not in cache, returning placeholder");
}
}
catch (Exception ex)
{
Trace.WriteLine($"[User] ⚠️ Ошибка загрузки аватара для {FullName}: {ex.Message}");
}
// Возвращаем placeholder
return ImageSource.FromFile("dotnet_bot.png");
}
}
[JsonIgnore]
public string DepartmentName
{
get
{
if (Departments != null && Departments.Count > 0)
return Departments[0].Name;
if (Department is Department dept)
return dept.Name;
if (Department is string str)
return str;
if (Department is JsonElement element)
{
if (element.ValueKind == JsonValueKind.String)
return element.GetString() ?? string.Empty;
if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty("name", out JsonElement nameProp))
return nameProp.GetString() ?? string.Empty;
}
return string.Empty;
}
}
[JsonIgnore]
public string PositionName
{
get
{
if (Position is Position pos)
return pos.Name;
if (Position is string str)
return str;
if (Position is JsonElement element)
{
if (element.ValueKind == JsonValueKind.String)
return element.GetString() ?? string.Empty;
if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty("name", out JsonElement nameProp))
return nameProp.GetString() ?? string.Empty;
}
return string.Empty;
}
}
}