Initial first version.

This commit is contained in:
Ebbe Baß
2026-08-13 10:04:59 +02:00
parent df725b43c4
commit 940e80b630
22 changed files with 890 additions and 0 deletions
@@ -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();
}
}
}