diff --git a/critterfolio/CritterFolio/CritterFolio/App.axaml.cs b/critterfolio/CritterFolio/CritterFolio/App.axaml.cs index 093ec86..b10e75e 100644 --- a/critterfolio/CritterFolio/CritterFolio/App.axaml.cs +++ b/critterfolio/CritterFolio/CritterFolio/App.axaml.cs @@ -1,3 +1,4 @@ +using System.IO; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core; @@ -37,6 +38,8 @@ public partial class App : Application } // Other init stuff here + Directory.CreateDirectory(Sys.UserDataPath); + Directory.CreateDirectory(Path.Combine(Sys.UserDataPath, "ImageCache")); base.OnFrameworkInitializationCompleted(); diff --git a/critterfolio/CritterFolio/CritterFolio/Controls/ProfileButton.axaml b/critterfolio/CritterFolio/CritterFolio/Controls/ProfileButton.axaml index 6d5166b..b042639 100644 --- a/critterfolio/CritterFolio/CritterFolio/Controls/ProfileButton.axaml +++ b/critterfolio/CritterFolio/CritterFolio/Controls/ProfileButton.axaml @@ -11,7 +11,7 @@ VerticalContentAlignment="Center" x:Name="MainButton"> - + diff --git a/critterfolio/CritterFolio/CritterFolio/DataModels/Document.cs b/critterfolio/CritterFolio/CritterFolio/DataModels/Document.cs index aa0802a..320d4f2 100644 --- a/critterfolio/CritterFolio/CritterFolio/DataModels/Document.cs +++ b/critterfolio/CritterFolio/CritterFolio/DataModels/Document.cs @@ -7,6 +7,7 @@ public class Document { [PrimaryKey, AutoIncrement] public int Id { get; set; } - - public string Name { get; set; } + public int CritterId { get; set; } + public string Name { get; set; } = "Document"; + public string Description { get; set; } = "A Document"; } \ No newline at end of file diff --git a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml index a4e5fb0..6ef1bbf 100644 --- a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml +++ b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml @@ -7,6 +7,7 @@ + @@ -27,6 +28,13 @@ + + + + + + + diff --git a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs index 23c6d8e..4d4658d 100644 --- a/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs +++ b/critterfolio/CritterFolio/CritterFolio/Pages/CritterPage.axaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Avalonia; @@ -7,6 +8,8 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform.Storage; using Avalonia.Threading; using CritterFolio.DataModels; using CritterFolio.Services; @@ -16,6 +19,7 @@ namespace CritterFolio.Pages; public partial class CritterPage : Page { private Critter? _critter; + private List _critterDisplays = []; public CritterPage() { @@ -25,32 +29,38 @@ public partial class CritterPage : Page public void Init(Critter critter) { _critter = critter; - - UpdateInfo(); + + PfpButton.Click += OpenFileButton_Clicked; } - public override void Refresh() + public override async void Refresh() { base.Refresh(); CreateHeaderButtons(); GenderOption.ItemsSource = Enum.GetNames(typeof(Gender)); - - LoadComboItemsAsync(); + + await UpdateInfo(); } - private async void LoadComboItemsAsync() + private async Task LoadComboItemsAsync() { + _critterDisplays.Clear(); + _critterDisplays.Add(new CritterComboBoxDisplay(0, "Not set")); + try { var allCritters = await DatabaseService.GetAllCritters(); - var allCritterIds = allCritters.Select(c => c.Id).ToList(); + + foreach (var critter in allCritters) + { + if (critter.Id == _critter?.Id) continue; + _critterDisplays.Add(new CritterComboBoxDisplay(critter.Id, critter.Name)); + } await Dispatcher.UIThread.InvokeAsync(() => { - FatherPicker.ItemsSource = allCritterIds; - MotherPicker.ItemsSource = allCritterIds; - - UpdateInfo(); + FatherPicker.ItemsSource = _critterDisplays; + MotherPicker.ItemsSource = _critterDisplays; }); } catch (Exception ex) @@ -59,15 +69,64 @@ public partial class CritterPage : Page } } - private void UpdateInfo() + private async Task UpdateInfo() { + if (_critter is null) return; + Sys.Navigation?.SetTitle(_critter.Name); + + await LoadComboItemsAsync(); NameBox.Text = _critter.Name; GenderOption.SelectedItem = Enum.GetName(typeof(Gender), _critter.Gender); BirthdayPicker.SelectedDate = _critter.DateOfBirth; - MotherPicker.SelectedItem = _critter.FatherId; - FatherPicker.SelectedItem = _critter.FatherId; + + if (_critter.FatherId != 0) + { + var father = _critterDisplays.FirstOrDefault(c => c.Id == _critter.FatherId); + if (father is null) + { + Logger.LogToFile($"Couldn't find father id: {_critter.FatherId}"); + await DialogHelper.ShowMessage($"ERROR: Couldn't find FatherID: {_critter.FatherId}"); + return; + } + FatherPicker.SelectedItem = father; + } + else + { + FatherPicker.SelectedItem = _critterDisplays.FirstOrDefault(c => c.Id == 0); + } + + if (_critter.MotherId != 0) + { + var mother = _critterDisplays.FirstOrDefault(c => c.Id == _critter.MotherId); + if (mother is null) + { + Logger.LogToFile($"[ERROR] Couldn't find MotherId: {_critter.MotherId}"); + await DialogHelper.ShowMessage($"ERROR: Couldn't find MotherId: {_critter.MotherId}"); + return; + } + MotherPicker.SelectedItem = mother; + } + else + { + MotherPicker.SelectedItem = _critterDisplays.FirstOrDefault(c => c.Id == 0); + } + + NotesBox.Text = _critter.Notes; + + if (!string.IsNullOrWhiteSpace(_critter.ProfileImagePath)) + { + try + { + var bmp = new Bitmap(_critter.ProfileImagePath); + ProfImage.Source = bmp; + } + catch (Exception e) + { + Console.WriteLine($"Pfp image for {_critter.Id} was moved, deleted, or is corrupt."); + } + } } private void CreateHeaderButtons() @@ -86,14 +145,13 @@ public partial class CritterPage : Page Classes = { "headerBttn" }, Content = "\ue4a6" }; - delBttn.Foreground = new SolidColorBrush(Colors.DarkRed); + delBttn.Foreground = new SolidColorBrush(Colors.Red); delBttn.Click += DeleteButtonClicked; Sys.Navigation?.AddHeaderButton(delBttn); } private async void DeleteButtonClicked(object? sender, RoutedEventArgs args) { - try { var result = await DialogHelper.ShowConfirmationDialog("Are you sure you want to delete this critter?"); @@ -125,7 +183,7 @@ public partial class CritterPage : Page } await DatabaseService.UpdateCritter(_critter); - UpdateInfo(); + await UpdateInfo(); await DialogHelper.ShowMessage($"Critter '{_critter.Name}' saved!"); } @@ -164,28 +222,81 @@ public partial class CritterPage : Page errString += "Birthday is invalid somehow. Please report this to the developer\n"; } - // Dont need to validate these yet, as they are not required - // // Father ID validation - // if (FatherPicker.SelectedItem is int fatherId) - // { - // _critter.FatherId = fatherId; - // } - // else - // { - // errString += "FatherId is null or invalid somehow. Please report this to the developer\n"; - // } - // - // // Mother ID validation - // if (MotherPicker.SelectedItem is int motherId) - // { - // _critter.MotherId = motherId; - // } - // else - // { - // errString += "MotherId is null or invalid somehow. Please report this to the developer\n"; - // } - + // TODO: Change the way that fatherid and motherid work, it should show names, not the ids, but still link back to the id + // Father ID validation + if (FatherPicker.SelectedItem is CritterComboBoxDisplay father) + { + var id = _critterDisplays.FirstOrDefault(d => d.Id == father.Id); + _critter.FatherId = id?.Id ?? 0; + } + else + { + _critter.FatherId = 0; + } + + // Mother ID validation + if (MotherPicker.SelectedItem is CritterComboBoxDisplay mother) + { + var id = _critterDisplays.FirstOrDefault(d => d.Id == mother.Id); + _critter.MotherId = id?.Id ?? 0; + } + else + { + _critter.MotherId = 0; + } + + if (_critter.MotherId == _critter.FatherId && _critter.MotherId > 0 && _critter.FatherId > 0) + { + errString += "Mother and Father cannot be the same"; + } + + _critter.Notes = NotesBox.Text ?? ""; + return errString != "" ? (false, errString) : (true, "No errors"); } + private class CritterComboBoxDisplay(int id, string name) + { + public int Id { get; set; } = id; + public string Name { get; set; } = name; + + public override string ToString() => Name; + } + + private async void OpenFileButton_Clicked(object? sender, RoutedEventArgs args) + { + var topLevel = TopLevel.GetTopLevel(this); + + var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions + { + Title = "Open Image File", + AllowMultiple = false, + FileTypeFilter = [new FilePickerFileType("image"){Patterns = ["*.png", "*.jpg", "*.jpeg"]}] + }); + + if (files.Count == 0) return; + + var file = files[0]; + + try + { + + await using var stream = await file.OpenReadAsync(); + // using var streamReader = new StreamReader(stream); + // var fileContent = await streamReader.ReadToEndAsync(); + + var bmp = new Bitmap(stream); + var path = Path.Combine(Sys.UserDataPath, "ImageCache", $"{_critter.Id}-{_critter.Name}.jpg"); + bmp.Save(path); + _critter.ProfileImagePath = path; + await UpdateInfo(); + } + catch (Exception e) + { + await DialogHelper.ShowMessage(e.ToString()); + } + + + } + } \ No newline at end of file