Continue working on main menu screen and refactor

New method for storing information on report data; rework and migrate settings to version 2; Add new options to ConfirmViewModel; rework some class hierarchy (may change more later)
This commit is contained in:
2026-03-30 20:12:34 +09:00
parent 8be518e81c
commit a9674a3f45
9 changed files with 247 additions and 73 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ public partial class App : Application, ITopLevelGrabber
public TopLevel GetTopLevel() public TopLevel GetTopLevel()
{ {
return _topLevel; return _topLevel!;
} }
public void AboutOnClick(object? sender, EventArgs args) public void AboutOnClick(object? sender, EventArgs args)
+2 -26
View File
@@ -9,47 +9,23 @@ using MayShow.Helpers;
namespace MayShow.Models; namespace MayShow.Models;
class PDFReport : ChangeNotifier class PDFReport : PDFReportInfo
{ {
private string _baseFolder;
private string _title;
private List<ReportFile> _files; private List<ReportFile> _files;
private DateTime _lastSaved;
private DateTime? _lastGenerated; private DateTime? _lastGenerated;
public PDFReport() public PDFReport() : base()
{ {
_baseFolder = "";
_title = "";
_files = []; _files = [];
_lastSaved = DateTime.Now;
_lastGenerated = null; _lastGenerated = null;
} }
public string BaseFolder
{
get => _baseFolder;
set { _baseFolder = value; NotifyPropertyChanged(); }
}
public string Title
{
get => _title;
set { _title = value; NotifyPropertyChanged(); }
}
public List<ReportFile> Files public List<ReportFile> Files
{ {
get => _files; get => _files;
set { _files = value; NotifyPropertyChanged(); } set { _files = value; NotifyPropertyChanged(); }
} }
public DateTime LastSaved
{
get => _lastSaved;
set { _lastSaved = value; NotifyPropertyChanged(); }
}
public DateTime? LastGenerated public DateTime? LastGenerated
{ {
get => _lastGenerated; get => _lastGenerated;
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using MayShow.Helpers;
using MayShows.Helpers;
namespace MayShow.Models;
class PDFReportInfo : ChangeNotifier
{
private string? _baseFolder; // might be null
private string _uuid;
private string _title;
private DateTime? _lastSaved;
public PDFReportInfo() : base()
{
_baseFolder = null;
_uuid = Guid.NewGuid().ToString();
_title = "";
_lastSaved = null;
}
public string? BaseFolder
{
get => _baseFolder;
set { _baseFolder = value; NotifyPropertyChanged(); }
}
public string UUID
{
get => _uuid;
set { _uuid = value; NotifyPropertyChanged(); }
}
public string Title
{
get => _title;
set { _title = value; NotifyPropertyChanged(); }
}
public DateTime? LastSaved
{
get => _lastSaved;
set { _lastSaved = value; NotifyPropertyChanged(); }
}
public void ResetUUID()
{
UUID = Guid.NewGuid().ToString();
}
public void DeleteInternalFolderFromDisk()
{
var path = Path.Combine(Utilities.GetInternalDataPath(), UUID);
if (Directory.Exists(path) && path != Utilities.GetInternalDataPath())
{
Directory.Delete(path, true);
}
}
}
+49 -5
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@@ -18,10 +19,11 @@ class Settings : ChangeNotifier
private string _outputPdfDir; private string _outputPdfDir;
private decimal _imageResizeThreshold; private decimal _imageResizeThreshold;
private bool _saveReportJsonDataInInternalDir; private bool _saveReportJsonDataInInternalDir;
private Dictionary<string, string> _workingFolderToInternalFolderName; private Dictionary<string, string> _workingFolderToInternalFolderName; // obsolete
private List<PDFReportInfo> _allReportInfo;
public int _settingsVersion; public int _settingsVersion;
public Settings() public Settings() : base()
{ {
_lastUsedPath = ""; _lastUsedPath = "";
_useDocnetPDFImageRendering = true; _useDocnetPDFImageRendering = true;
@@ -30,7 +32,8 @@ class Settings : ChangeNotifier
_imageResizeThreshold = 1.5m; _imageResizeThreshold = 1.5m;
_saveReportJsonDataInInternalDir = false; _saveReportJsonDataInInternalDir = false;
_workingFolderToInternalFolderName = []; _workingFolderToInternalFolderName = [];
_settingsVersion = 1; _allReportInfo = [];
_settingsVersion = 2;
} }
public Settings(Settings other) public Settings(Settings other)
@@ -43,6 +46,7 @@ class Settings : ChangeNotifier
_saveReportJsonDataInInternalDir = other.SaveReportJsonDataInInternalDir; _saveReportJsonDataInInternalDir = other.SaveReportJsonDataInInternalDir;
_workingFolderToInternalFolderName = other.WorkingFolderToInternalFolderName; _workingFolderToInternalFolderName = other.WorkingFolderToInternalFolderName;
_settingsVersion = other.SettingsVersion; _settingsVersion = other.SettingsVersion;
_allReportInfo = other.AllReportInfo;
} }
[JsonInclude] [JsonInclude]
@@ -95,6 +99,13 @@ class Settings : ChangeNotifier
set { _workingFolderToInternalFolderName = value; NotifyPropertyChanged(); } set { _workingFolderToInternalFolderName = value; NotifyPropertyChanged(); }
} }
[JsonInclude]
public List<PDFReportInfo> AllReportInfo
{
get => _allReportInfo;
set { _allReportInfo = value; NotifyPropertyChanged(); }
}
[JsonInclude] [JsonInclude]
public int SettingsVersion public int SettingsVersion
{ {
@@ -135,6 +146,39 @@ class Settings : ChangeNotifier
return json; return json;
} }
private static Settings UpgradeSettings(Settings settings)
{
if (settings.SettingsVersion == 1)
{
// update settings
var internalPath = Utilities.GetInternalDataPath();
var list = new List<PDFReportInfo>();
foreach (var data in settings.WorkingFolderToInternalFolderName)
{
var uuid = data.Value;
var path = Path.Combine(internalPath, uuid, Constants.ReportSavedDataFileName);
var json = File.ReadAllText(path);
var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
var report = File.Exists(path) ? JsonSerializer.Deserialize(json, jsonContext.PDFReport) : null;
var reportTitle = report?.Title ?? "";
var lastSaved = report?.LastSaved;
var reportInfo = new PDFReportInfo()
{
Title = reportTitle,
UUID = uuid,
LastSaved = lastSaved,
BaseFolder = data.Key,
};
list.Add(reportInfo);
}
settings.AllReportInfo = list.OrderBy(x => x.Title).ToList();
settings.WorkingFolderToInternalFolderName = []; // clear this list; it is no longer used
settings.SettingsVersion = 2;
// TODO: finish using new array for everything, make sure we save data, etc.
}
return settings;
}
public static Settings LoadSettings() public static Settings LoadSettings()
{ {
var path = GetSettingsPath(); var path = GetSettingsPath();
@@ -144,7 +188,7 @@ class Settings : ChangeNotifier
} }
var json = File.ReadAllText(GetSettingsPath()); var json = File.ReadAllText(GetSettingsPath());
var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions()); var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
return JsonSerializer.Deserialize<Settings>(json, jsonContext.Settings) ?? new Settings(); return UpgradeSettings(JsonSerializer.Deserialize<Settings>(json, jsonContext.Settings) ?? new Settings());
} }
public static async Task<Settings> LoadSettingsAsync() public static async Task<Settings> LoadSettingsAsync()
@@ -152,6 +196,6 @@ class Settings : ChangeNotifier
using FileStream fileStream = File.OpenRead(GetSettingsPath()); using FileStream fileStream = File.OpenRead(GetSettingsPath());
var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions()); var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
var output = await JsonSerializer.DeserializeAsync<Settings>(fileStream, jsonContext.Settings) ?? new Settings(); var output = await JsonSerializer.DeserializeAsync<Settings>(fileStream, jsonContext.Settings) ?? new Settings();
return output; return UpgradeSettings(output);
} }
} }
@@ -5,12 +5,14 @@ using MayShow.Helpers;
namespace MayShow.ViewModels; namespace MayShow.ViewModels;
class ConfirmViewModel class ConfirmViewModel : ChangeNotifier
{ {
private string _title; private string _title;
private string _message; private string _message;
private string _confirmTitle; private string _confirmTitle;
private string _declineTitle; private string _declineTitle;
private bool _confirmButtonUsesDangerStyle;
private string _confirmButtonIcon;
public ConfirmViewModel(string title, string message, string confirmTitle = "Yes", string declineTitle = "No") public ConfirmViewModel(string title, string message, string confirmTitle = "Yes", string declineTitle = "No")
{ {
@@ -18,6 +20,8 @@ class ConfirmViewModel
_message = message; _message = message;
_confirmTitle = confirmTitle; _confirmTitle = confirmTitle;
_declineTitle = declineTitle; _declineTitle = declineTitle;
_confirmButtonUsesDangerStyle = false;
_confirmButtonIcon = "";
} }
public string Title public string Title
@@ -40,6 +44,35 @@ class ConfirmViewModel
get => _declineTitle; get => _declineTitle;
} }
public bool ConfirmButtonIsAccent
{
get => !_confirmButtonUsesDangerStyle;
}
public bool ConfirmButtonIsDanger
{
get => _confirmButtonUsesDangerStyle;
}
public bool ConfirmButtonUsesDangerStyle
{
set
{
_confirmButtonUsesDangerStyle = value;
NotifyPropertyChanged(nameof(ConfirmButtonIsAccent));
NotifyPropertyChanged(nameof(ConfirmButtonIsDanger));
}
}
public string ConfirmTitleIcon
{
get => _confirmButtonIcon;
set
{
_confirmButtonIcon = value; NotifyPropertyChanged();
}
}
public void Confirm() public void Confirm()
{ {
DialogHost.Close("DialogHost", true); DialogHost.Close("DialogHost", true);
@@ -253,13 +253,13 @@ class CreatePDFReportViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
// load prior report // load prior report
var json = File.ReadAllText(reportFilePath); var json = File.ReadAllText(reportFilePath);
var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions()); var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
var report = JsonSerializer.Deserialize<PDFReport>(json, jsonContext.PDFReport); var report = JsonSerializer.Deserialize(json, jsonContext.PDFReport);
if (report != null && report.Files.Count > 0) if (report != null && report.Files.Count > 0)
{ {
Console.WriteLine("Loading prior report data at {0}", reportFilePath); Console.WriteLine("Loading prior report data at {0}", reportFilePath);
ReportFiles = new ObservableCollection<ReportFile>(report.Files); ReportFiles = new ObservableCollection<ReportFile>(report.Files);
ReportTitle = report.Title; ReportTitle = report.Title;
WorkingFolder = report.BaseFolder; WorkingFolder = report.BaseFolder ?? "";
_lastGeneratedTime = report.LastGenerated ?? null; _lastGeneratedTime = report.LastGenerated ?? null;
LogInfo("Reloaded report last saved at {0}", report.LastSaved); LogInfo("Reloaded report last saved at {0}", report.LastSaved);
successfullyLoadedPriorReport = true; successfullyLoadedPriorReport = true;
@@ -1,43 +1,30 @@
#nullable enable #nullable enable
using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Avalonia.Platform.Storage;
using Avalonia.Themes.Fluent;
using DialogHostAvalonia; using DialogHostAvalonia;
using ImageMagick;
using MigraDoc.DocumentObjectModel;
using MigraDoc.Rendering;
using PdfSharp.Fonts;
using PdfSharp.Pdf.IO;
using PdfSharp.Snippets.Font;
using MayShow.Interfaces; using MayShow.Interfaces;
using MayShow.Models; using MayShow.Models;
using MayShow.Helpers; using MayShow.Helpers;
using MayShows.Helpers;
namespace MayShow.ViewModels; namespace MayShow.ViewModels;
class StartNewChooseReportViewModel : BaseViewModel class StartNewChooseReportViewModel : BaseViewModel
{ {
private string _creatingReportTitle; private string _creatingReportTitle;
private ObservableCollection<string> _savedReports; private ObservableCollection<PDFReportInfo> _savedReports;
private Settings _settings;
public StartNewChooseReportViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger) public StartNewChooseReportViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger)
{ {
_creatingReportTitle = ""; _creatingReportTitle = "";
// TODO: load existing reports _settings = Settings.LoadSettings();
_savedReports = []; _savedReports = new ObservableCollection<PDFReportInfo>(_settings.AllReportInfo.OrderBy(x => x.Title));
for (var i = 1; i <= 100; i++)
{
_savedReports.Add("Report " + i);
}
} }
public string Version public static string Version
{ {
get => Constants.AppVersion; get => Constants.AppVersion;
} }
@@ -48,30 +35,66 @@ class StartNewChooseReportViewModel : BaseViewModel
set { _creatingReportTitle = value; NotifyPropertyChanged(); } set { _creatingReportTitle = value; NotifyPropertyChanged(); }
} }
public ObservableCollection<string> SavedReports public ObservableCollection<PDFReportInfo> SavedReports
{ {
get => _savedReports; get => _savedReports;
set { _savedReports = value; NotifyPropertyChanged(); } set { _savedReports = value; NotifyPropertyChanged(); }
} }
public void StartReport() public async void StartReport()
{ {
// TODO: make sure there is a folder and everything set up for this report var reportInfo = new PDFReportInfo()
{
Title = CreatingReportTitle,
};
_settings.AllReportInfo.Add(reportInfo);
// ... this sort is slow, technically, but we're not going to have millions of items here, so...
SavedReports = new ObservableCollection<PDFReportInfo>(_settings.AllReportInfo.OrderBy(x => x.Title));
await _settings.SaveSettingsAsync();
// create folder for report data
var path = Path.Combine(Utilities.GetInternalDataPath(), reportInfo.UUID);
while (Directory.Exists(path))
{
reportInfo.ResetUUID();
path = Path.Combine(Utilities.GetInternalDataPath(), reportInfo.UUID);
}
Directory.CreateDirectory(path);
// now update UI
ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(ViewModelChanger) ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(ViewModelChanger)
{ {
ReportTitle = CreatingReportTitle ReportTitle = CreatingReportTitle
}); });
CreatingReportTitle = ""; // when user comes back they can start another new report CreatingReportTitle = ""; // when user comes back they can start another new report
// TODO: add to existing reports list
} }
public void LoadExistingReport()
public void LoadExistingReport(object info) => LoadExistingReportImpl((PDFReportInfo) info);
public void LoadExistingReportImpl(PDFReportInfo reportInfo)
{ {
// TODO: load data and send to create PDF report view model // TODO: load data and send to create PDF report view model
} }
public void DeleteExistingReport() public void DeleteExistingReport(object info) => DeleteExistingReportImpl((PDFReportInfo) info);
public async void DeleteExistingReportImpl(PDFReportInfo reportInfo)
{ {
// TODO: warn user, delete if they want to proceed var message = string.IsNullOrWhiteSpace(reportInfo.BaseFolder)
? "Are you sure you want to delete this report and its associated data? It will be gone forever!"
: "Are you sure you want to delete information about this report? It will be gone forever!";
var result = await DialogHost.Show(new ConfirmViewModel(
"Warning!",
message,
"Delete Report",
"Cancel")
{
ConfirmButtonUsesDangerStyle = true,
ConfirmTitleIcon = "\uf1f8;"
});
if (result != null && (bool)result)
{
SavedReports.Remove(reportInfo);
_settings.AllReportInfo.Remove(reportInfo);
reportInfo.DeleteInternalFolderFromDisk(); // delete internal data if available
await _settings.SaveSettingsAsync(); // update saved items list
}
} }
} }
+9 -4
View File
@@ -20,16 +20,21 @@
MaxWidth="350" MaxWidth="350"
Text="{Binding Message}"/> Text="{Binding Message}"/>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Spacing="12" Spacing="16"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Margin="4"> Margin="4">
<Button Command="{Binding Decline}" <Button Command="{Binding Decline}"
Content="{Binding DeclineTitle}" Content="{Binding DeclineTitle}"
HorizontalAlignment="Right"/> HorizontalAlignment="Right"/>
<Button Command="{Binding Confirm}" <Button Command="{Binding Confirm}"
Classes="accent" Classes.accent="{Binding ConfirmButtonIsAccent}"
Content="{Binding ConfirmTitle}" Classes.Danger="{Binding ConfirmButtonIsDanger}"
HorizontalAlignment="Right"/> HorizontalAlignment="Right">
<TextBlock>
<Run Text="{Binding ConfirmTitleIcon}"
FontFamily="{StaticResource FontAwesomeSolid}" /> <Run Text="{Binding ConfirmTitle}"/>
</TextBlock>
</Button>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>
@@ -26,7 +26,7 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
Grid.Column="1" Grid.Column="1"
Grid.Row="1" /> Grid.Row="1" />
<Label Content="Start New Project" <Label Content="Start New Report"
HorizontalAlignment="Left" HorizontalAlignment="Left"
FontSize="18" FontSize="18"
FontWeight="Bold" FontWeight="Bold"
@@ -38,7 +38,7 @@
Margin="0,8,0,0" Margin="0,8,0,0"
Grid.Column="1" Grid.Column="1"
Grid.Row="3"> Grid.Row="3">
<TextBox Watermark="Report Title" <TextBox Watermark="Feb 2024 Report"
Classes="clearButton" Classes="clearButton"
Text="{Binding CreatingReportTitle}" Text="{Binding CreatingReportTitle}"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
@@ -54,14 +54,14 @@
FontFamily="{StaticResource FontAwesomeSolid}" /> Create and Start Report</TextBlock> FontFamily="{StaticResource FontAwesomeSolid}" /> Create and Start Report</TextBlock>
</Button> </Button>
</Grid> </Grid>
<Label Content="Load Existing Project" <Label Content="Load Existing Report"
FontSize="18" FontSize="18"
FontWeight="Bold" FontWeight="Bold"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="0,16,0,0" Margin="0,16,0,0"
Grid.Column="1" Grid.Column="1"
Grid.Row="4"/> Grid.Row="4"/>
<ScrollViewer Margin="0,8,0,8" <ScrollViewer Margin="0,8,0,32"
Grid.Column="1" Grid.Column="1"
Grid.Row="5" Grid.Row="5"
VerticalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"
@@ -69,10 +69,38 @@
HorizontalScrollBarVisibility="Disabled"> HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding SavedReports}"> <ItemsControl ItemsSource="{Binding SavedReports}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate x:DataType="models:PDFReportInfo">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Vertical"
<TextBlock Text="{Binding}" /> Margin="12,4,4,8"
<!-- TODO: buttons to load, delete reports --> Spacing="4">
<TextBlock Text="{Binding Title}" />
<!-- <TextBlock Text="{Binding BaseFolder}"
Foreground="Gray"
TextWrapping="Wrap" /> -->
<TextBlock Foreground="Gray"
TextWrapping="Wrap">
Last saved on: <Run Text="{Binding LastSaved, StringFormat='{}{0:yyyy-MM-dd}'}"/>
</TextBlock>
<StackPanel Orientation="Horizontal"
Spacing="8">
<Button Command="{Binding $parent[UserControl].((vm:StartNewChooseReportViewModel)DataContext).LoadExistingReport}"
CommandParameter="{Binding}"
Classes="accent"
Grid.Column="1">
<TextBlock>
<Run Text="&#xf56f;"
FontFamily="{StaticResource FontAwesomeSolid}" /> Load Report</TextBlock>
</Button>
<Button Command="{Binding $parent[UserControl].((vm:StartNewChooseReportViewModel)DataContext).DeleteExistingReport}"
CommandParameter="{Binding}"
Classes="Danger"
Grid.Column="1">
<TextBlock>
<Run Text="&#xf1f8;"
FontFamily="{StaticResource FontAwesomeSolid}" /> Delete Report</TextBlock>
</Button>
</StackPanel>
<Rectangle Fill="Gray" Height="2" HorizontalAlignment="Stretch" Margin="0,8,0,0"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>