Initial first version.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
using System.Windows.Media.Imaging;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using EbbesMemeClipboard.Models;
|
||||
using EbbesMemeClipboard.Services;
|
||||
|
||||
namespace EbbesMemeClipboard.ViewModels;
|
||||
|
||||
public partial class MemeTileViewModel : ObservableObject
|
||||
{
|
||||
private const int ThumbnailPixelWidth = 150;
|
||||
|
||||
public LocalMemeRecord Record { get; }
|
||||
public string FullPath { get; }
|
||||
public string DisplayName => Record.OriginalFileName;
|
||||
|
||||
[ObservableProperty]
|
||||
private BitmapSource? _thumbnail;
|
||||
|
||||
public MemeTileViewModel(LocalMemeRecord record, string fullPath)
|
||||
{
|
||||
Record = record;
|
||||
FullPath = fullPath;
|
||||
}
|
||||
|
||||
public async Task LoadThumbnailAsync()
|
||||
{
|
||||
var path = FullPath;
|
||||
Thumbnail = await Task.Run(() => ImageDecoding.DecodeFirstFrame(path, ThumbnailPixelWidth));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using EbbesMemeClipboard.Services;
|
||||
|
||||
namespace EbbesMemeClipboard.ViewModels;
|
||||
|
||||
public partial class PickerViewModel : ObservableObject
|
||||
{
|
||||
private readonly ILocalMemeLibraryService _library;
|
||||
private readonly IClipboardService _clipboard;
|
||||
|
||||
public ObservableCollection<MemeTileViewModel> Items { get; } = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private string _searchText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _statusMessage = string.Empty;
|
||||
|
||||
public event EventHandler? RequestClose;
|
||||
|
||||
public PickerViewModel(ILocalMemeLibraryService library, IClipboardService clipboard)
|
||||
{
|
||||
_library = library;
|
||||
_clipboard = clipboard;
|
||||
RefreshItems();
|
||||
}
|
||||
|
||||
partial void OnSearchTextChanged(string value) => RefreshItems();
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SelectItemAsync(MemeTileViewModel? tile)
|
||||
{
|
||||
if (tile is null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await _clipboard.CopyLocalFileAsync(tile.FullPath);
|
||||
StatusMessage = $"Copied {tile.DisplayName}";
|
||||
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
StatusMessage = $"Couldn't copy {tile.DisplayName}: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ImportFilesAsync()
|
||||
{
|
||||
var dialog = new Microsoft.Win32.OpenFileDialog
|
||||
{
|
||||
Multiselect = true,
|
||||
Filter = "Images and GIFs|*.jpg;*.jpeg;*.png;*.gif",
|
||||
Title = "Add memes",
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
await ImportPathsAsync(dialog.FileNames);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ImportPathsAsync(IEnumerable<string> paths)
|
||||
{
|
||||
var candidates = paths
|
||||
.Where(p => Path.GetExtension(p).ToLowerInvariant() is ".jpg" or ".jpeg" or ".png" or ".gif")
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
StatusMessage = "No supported image/GIF files in that selection.";
|
||||
return;
|
||||
}
|
||||
|
||||
var imported = await _library.ImportAsync(candidates);
|
||||
RefreshItems();
|
||||
StatusMessage = $"Added {imported.Count} meme(s).";
|
||||
}
|
||||
|
||||
public void OnShown()
|
||||
{
|
||||
SearchText = string.Empty;
|
||||
RefreshItems();
|
||||
}
|
||||
|
||||
private void RefreshItems()
|
||||
{
|
||||
var records = string.IsNullOrWhiteSpace(SearchText)
|
||||
? _library.GetAll()
|
||||
: _library.Search(SearchText);
|
||||
|
||||
Items.Clear();
|
||||
foreach (var record in records)
|
||||
{
|
||||
var tile = new MemeTileViewModel(record, _library.GetFullPath(record));
|
||||
Items.Add(tile);
|
||||
_ = tile.LoadThumbnailAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user