Files
BookReader/BookReader/ViewModels/BookshelfViewModel.cs
Курнат Андрей 5c45538c2f qwen edit
2026-02-18 15:13:36 +03:00

223 lines
6.5 KiB
C#

using BookReader.Models;
using BookReader.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace BookReader.ViewModels;
public partial class BookshelfViewModel : BaseViewModel
{
private readonly IDatabaseService _databaseService;
private readonly IBookParserService _bookParserService;
private readonly ISettingsService _settingsService;
private readonly INavigationService _navigationService;
private readonly ICachedImageLoadingService _imageLoadingService;
public ObservableCollection<Book> Books { get; } = new();
[ObservableProperty]
private bool _isEmpty;
[ObservableProperty]
private bool _isRefreshing;
[ObservableProperty]
private string _searchText = string.Empty;
partial void OnSearchTextChanged(string value)
{
// Автоматически выполняем поиск при изменении текста
if (string.IsNullOrWhiteSpace(value))
{
// Если поле пустое - загружаем все книги
LoadBooksCommand.Execute(null);
}
}
public BookshelfViewModel(
IDatabaseService databaseService,
IBookParserService bookParserService,
ISettingsService settingsService,
INavigationService navigationService,
ICachedImageLoadingService imageLoadingService)
{
_databaseService = databaseService;
_bookParserService = bookParserService;
_settingsService = settingsService;
_navigationService = navigationService;
_imageLoadingService = imageLoadingService;
Title = "My Library";
}
[RelayCommand]
public async Task LoadBooksAsync()
{
if (IsBusy) return;
IsBusy = true;
try
{
var books = await _databaseService.GetAllBooksAsync();
// Применяем фильтр поиска если есть
if (!string.IsNullOrWhiteSpace(SearchText))
{
var searchLower = SearchText.ToLowerInvariant();
books = books.Where(b =>
b.Title.ToLowerInvariant().Contains(searchLower) ||
b.Author.ToLowerInvariant().Contains(searchLower)
).ToList();
}
Books.Clear();
foreach (var book in books)
{
Books.Add(book);
}
IsEmpty = Books.Count == 0;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error loading books: {ex.Message}");
}
finally
{
IsBusy = false;
}
}
[RelayCommand]
public async Task RefreshBooksAsync()
{
if (IsRefreshing) return;
IsRefreshing = true;
try
{
await LoadBooksAsync();
}
finally
{
IsRefreshing = false;
}
}
[RelayCommand]
public async Task SearchAsync(object? parameter)
{
// Если параметр пустой или null, используем текущий SearchText
var searchText = parameter?.ToString() ?? SearchText;
if (string.IsNullOrWhiteSpace(searchText))
{
// Очищаем поиск и загружаем все книги
SearchText = string.Empty;
}
await LoadBooksAsync();
}
[RelayCommand]
public async Task AddBookFromFileAsync()
{
try
{
var customFileTypes = new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Android, new[] { "application/epub+zip", "application/x-fictionbook+xml", "application/octet-stream", "*/*" } }
});
var result = await FilePicker.Default.PickAsync(new PickOptions
{
PickerTitle = "Select a book",
FileTypes = customFileTypes
});
if (result == null) return;
var extension = Path.GetExtension(result.FileName).ToLowerInvariant();
if (extension != ".epub" && extension != ".fb2")
{
await _navigationService.DisplayAlertAsync("Error", "Only EPUB and FB2 formats are supported.", "OK");
return;
}
IsBusy = true;
StatusMessage = "Adding book...";
// Copy to temp if needed and parse
string filePath;
using var stream = await result.OpenReadAsync();
var tempPath = Path.Combine(FileSystem.CacheDirectory, result.FileName);
using (var fileStream = File.Create(tempPath))
{
await stream.CopyToAsync(fileStream);
}
filePath = tempPath;
var book = await _bookParserService.ParseAndStoreBookAsync(filePath, result.FileName);
Books.Insert(0, book);
IsEmpty = false;
// Clean temp
try { File.Delete(tempPath); } catch { }
}
catch (Exception ex)
{
await _navigationService.DisplayAlertAsync("Error", $"Failed to add book: {ex.Message}", "OK");
}
finally
{
IsBusy = false;
StatusMessage = string.Empty;
}
}
[RelayCommand]
public async Task DeleteBookAsync(Book book)
{
if (book == null) return;
var confirm = await _navigationService.DisplayAlertAsync("Delete Book",
$"Are you sure you want to delete \"{book.Title}\"?", "Delete", "Cancel");
if (!confirm) return;
try
{
await _databaseService.DeleteBookAsync(book);
Books.Remove(book);
IsEmpty = Books.Count == 0;
}
catch (Exception ex)
{
await _navigationService.DisplayAlertAsync("Error", $"Failed to delete book: {ex.Message}", "OK");
}
}
[RelayCommand]
public async Task OpenBookAsync(Book book)
{
if (book == null) return;
var navigationParameter = new Dictionary<string, object>
{
{ "Book", book }
};
await _navigationService.GoToAsync("reader", navigationParameter);
}
[RelayCommand]
public async Task OpenSettingsAsync()
{
await _navigationService.GoToAsync("settings");
}
[RelayCommand]
public async Task OpenCalibreLibraryAsync()
{
await _navigationService.GoToAsync("calibre");
}
}