167 lines
6.9 KiB
C#
167 lines
6.9 KiB
C#
using System.Collections.ObjectModel;
|
||
using System.Diagnostics;
|
||
using CORP_MAUI.Models;
|
||
using CORP_MAUI.ViewModels;
|
||
|
||
namespace CORP_MAUI.Pages;
|
||
|
||
public partial class PostsPage
|
||
{
|
||
private readonly ObservableCollection<PostViewModel> _posts;
|
||
private int _currentPage = 1;
|
||
private bool _isLoading;
|
||
|
||
public PostsPage()
|
||
{
|
||
InitializeComponent();
|
||
|
||
_posts = new ObservableCollection<PostViewModel>();
|
||
PostsCollectionView.ItemsSource = _posts;
|
||
}
|
||
|
||
// Добавляем метод OnAppearing, чтобы обновлять данные при показе страницы, если нужно
|
||
protected override void OnAppearing()
|
||
{
|
||
base.OnAppearing();
|
||
if (_posts.Count == 0)
|
||
{
|
||
_ = LoadPostsAsync();
|
||
}
|
||
}
|
||
|
||
private async Task LoadPostsAsync(bool refresh = false)
|
||
{
|
||
if (_isLoading) return;
|
||
_isLoading = true;
|
||
|
||
if (refresh)
|
||
{
|
||
_currentPage = 1;
|
||
}
|
||
|
||
try
|
||
{
|
||
LoadingIndicator.IsVisible = true;
|
||
LoadingIndicator.IsRunning = true;
|
||
|
||
PaginatedResponse<Post> response = await ServiceProvider.ApiService.GetPostsPaginatedAsync(_currentPage);
|
||
|
||
if (refresh)
|
||
{
|
||
_posts.Clear();
|
||
}
|
||
|
||
if (response.Results != null)
|
||
{
|
||
IEnumerable<Task<PostViewModel>> tasks = response.Results.Select(async post =>
|
||
{
|
||
PostViewModel vm = new(post);
|
||
|
||
if (!string.IsNullOrEmpty(post.DisplayImageUrl))
|
||
{
|
||
try
|
||
{
|
||
Uri uri = new Uri(post.DisplayImageUrl);
|
||
// Sanitize filename & use extension from URL if available, else .jpg
|
||
string extension = Path.GetExtension(uri.LocalPath);
|
||
if (string.IsNullOrEmpty(extension)) extension = ".jpg";
|
||
|
||
string fileName = Path.GetFileNameWithoutExtension(uri.LocalPath);
|
||
fileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (current, invalidChar) => current.Replace(invalidChar, '_'));
|
||
|
||
string cacheFileName = $"post_img_{post.Id}_{fileName}{extension}";
|
||
string cacheFilePath = Path.Combine(FileSystem.CacheDirectory, cacheFileName);
|
||
|
||
bool isCached = false;
|
||
|
||
// 1. Проверяем кэш
|
||
if (File.Exists(cacheFilePath))
|
||
{
|
||
FileInfo fileInfo = new FileInfo(cacheFilePath);
|
||
if (fileInfo.Length > 0)
|
||
{
|
||
Trace.WriteLine($"[PostsPage] Изображение из кэша: {cacheFilePath}");
|
||
vm.LocalImagePath = cacheFilePath;
|
||
isCached = true;
|
||
}
|
||
}
|
||
|
||
// 2. Если нет в кэше - загружаем
|
||
if (!isCached)
|
||
{
|
||
Trace.WriteLine($"[PostsPage] Загрузка изображения: {post.DisplayImageUrl}");
|
||
byte[]? bytes = await ServiceProvider.ApiService.GetImageBytesAsync(post.DisplayImageUrl);
|
||
if (bytes is { Length: > 0 })
|
||
{
|
||
// Basic signature check
|
||
bool isLikelyImage = false;
|
||
if (bytes.Length > 2)
|
||
{
|
||
if (bytes[0] == 0xFF && bytes[1] == 0xD8) isLikelyImage = true; // JPEG
|
||
else if (bytes[0] == 0x89 && bytes[1] == 0x50) isLikelyImage = true; // PNG
|
||
else isLikelyImage = true; // Others
|
||
|
||
// Check for HTML
|
||
if (bytes.Length > 5 && bytes[0] == '<') isLikelyImage = false;
|
||
}
|
||
|
||
if (isLikelyImage)
|
||
{
|
||
await File.WriteAllBytesAsync(cacheFilePath, bytes);
|
||
Trace.WriteLine($"[PostsPage] Сохранено в кэш: {cacheFilePath}");
|
||
vm.LocalImagePath = cacheFilePath;
|
||
}
|
||
else
|
||
{
|
||
Trace.WriteLine($"[PostsPage] ⚠️ Received bytes are likely NOT an image (starts with {bytes[0]:X}), skipping cache.");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch(Exception imgEx)
|
||
{
|
||
Trace.WriteLine($"[PostsPage] Ошибка обработки изображения для поста {post.Id}: {imgEx.Message}");
|
||
}
|
||
}
|
||
return vm;
|
||
});
|
||
|
||
// Ждем загрузки всех изображений перед отображением списка
|
||
PostViewModel[] viewModels = await Task.WhenAll(tasks);
|
||
|
||
foreach (PostViewModel vm in viewModels)
|
||
{
|
||
_posts.Add(vm);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Trace.WriteLine($"Ошибка загрузки постов: {ex.Message}");
|
||
await DisplayAlert("Ошибка", "Не удалось загрузить новости", "OK");
|
||
}
|
||
finally
|
||
{
|
||
_isLoading = false;
|
||
LoadingIndicator.IsVisible = false;
|
||
LoadingIndicator.IsRunning = false;
|
||
PostsRefreshView.IsRefreshing = false;
|
||
}
|
||
}
|
||
|
||
private async void OnRefreshing(object sender, EventArgs e)
|
||
{
|
||
await LoadPostsAsync(true);
|
||
}
|
||
|
||
private async void OnPostSelected(object sender, SelectionChangedEventArgs e)
|
||
{
|
||
if (e.CurrentSelection.FirstOrDefault() is not PostViewModel selectedPost) return;
|
||
// Навигация на страницу деталей
|
||
await Navigation.PushAsync(new PostDetailsPage(selectedPost));
|
||
|
||
// Снимаем выделение
|
||
PostsCollectionView.SelectedItem = null;
|
||
}
|
||
}
|