diff --git a/TODO.txt b/TODO.txt index 0a1edf5..94db5fe 100644 --- a/TODO.txt +++ b/TODO.txt @@ -6,12 +6,16 @@ -cleanup empty uuid folders in case user gets an internal folder created but never saves -add dropdown to Add Items button to have add items from folder and remove choose working folder option (data always saved internally) --always save report data (file locations, title, etc.) to internal dir (might already be done?) +*-always save report data (file locations, title, etc.) to internal dir (might already be done?) -no more option to save data internally -> ALWAYS save report_data.json internally +-Now that the BaseFolder is not really set...update option for PDF output + -output in internal dir + -always ask me every time + -always put in X folder -update project title -> should update recently used data -this sort of works, something is wrong with the upgrade process where the UUID is not brought over properly; need to test and fix (maybe fixed already and my dataset is wrong?) -add option to backup added files to internal data directory --make backup of last generated PDF +-make backup of last generated PDF somewhere -iOS-specific (MAUI essentials?) -Take picture -Add pic from gallery diff --git a/src/MayShow.Shared/Helpers/Utilities.cs b/src/MayShow.Shared/Helpers/Utilities.cs index a092737..efad8a9 100644 --- a/src/MayShow.Shared/Helpers/Utilities.cs +++ b/src/MayShow.Shared/Helpers/Utilities.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; @@ -7,9 +8,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using System.Text.RegularExpressions; -using Avalonia.Utilities; +using System.Threading.Tasks; using MayShow.Models; -using Tmds.DBus.Protocol; namespace MayShow.Helpers; @@ -115,7 +115,7 @@ class Utilities { if (context == null) { - var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions()); + var jsonContext = new SourceGenerationContext(GetSerializerOptions()); context = jsonContext.PDFReport; } using var memoryStream = new MemoryStream(); @@ -125,4 +125,19 @@ class Utilities var updatedJson = reader.ReadToEnd(); File.WriteAllText(path, updatedJson); } + + public static async Task SaveReportDataAsync(PDFReport reportData, string path, JsonTypeInfo? context = null) + { + if (context == null) + { + var jsonContext = new SourceGenerationContext(GetSerializerOptions()); + context = jsonContext.PDFReport; + } + using var memoryStream = new MemoryStream(); + await JsonSerializer.SerializeAsync(memoryStream, reportData, context); + memoryStream.Position = 0; + using var reader = new StreamReader(memoryStream); + var json = await reader.ReadToEndAsync(); + await File.WriteAllTextAsync(path, json); + } } \ No newline at end of file diff --git a/src/MayShow.Shared/Models/PDFReportInfo.cs b/src/MayShow.Shared/Models/PDFReportInfo.cs index 4e0702f..ed0ca87 100644 --- a/src/MayShow.Shared/Models/PDFReportInfo.cs +++ b/src/MayShow.Shared/Models/PDFReportInfo.cs @@ -11,20 +11,21 @@ namespace MayShow.Models; class PDFReportInfo : ChangeNotifier { - private string? _baseFolder; // might be null; if set, use this to know where (initial) working dir is + private string _baseFolder; private string _uuid; private string _title; private DateTime? _lastSaved; public PDFReportInfo() : base() { - _baseFolder = null; _uuid = Guid.NewGuid().ToString(); + _baseFolder = ""; + UpdateBaseFolder(); _title = ""; _lastSaved = null; } - public string? BaseFolder + public string BaseFolder { get => _baseFolder; set { _baseFolder = value; NotifyPropertyChanged(); } @@ -48,6 +49,11 @@ class PDFReportInfo : ChangeNotifier set { _lastSaved = value; NotifyPropertyChanged(); } } + public void UpdateBaseFolder() + { + _baseFolder = Path.Combine(Utilities.GetInternalDataPath(), _uuid); + } + public void ResetUUID() { UUID = Guid.NewGuid().ToString(); diff --git a/src/MayShow.Shared/Models/ReportPDFCreator.cs b/src/MayShow.Shared/Models/ReportPDFCreator.cs index 89af843..fc417ed 100644 --- a/src/MayShow.Shared/Models/ReportPDFCreator.cs +++ b/src/MayShow.Shared/Models/ReportPDFCreator.cs @@ -83,10 +83,10 @@ class ReportPDFCreator : ChangeNotifier } // https://forum.pdfsharp.net/viewtopic.php?f=2&t=1025 - public async Task CreatePDF(List reportFiles, string reportTitle, string workingDirPath, PDFFontResolver fontResolver, Settings appSettings) + public async Task CreatePDF(List reportFiles, string reportTitle, string outputFolderPath, PDFFontResolver fontResolver, Settings appSettings) { // safety checks - var outputDir = appSettings.SaveOutputPdfInWorkingDir ? workingDirPath : appSettings.OutputPdfDir; + var outputDir = appSettings.SaveOutputPdfInWorkingDir ? outputFolderPath : appSettings.OutputPdfDir; if (!Directory.Exists(outputDir)) { await DialogHost.Show(new WarningViewModel("Output directory not found! Please adjust your application Settings before continuing. Output directory: " + outputDir)); @@ -111,7 +111,7 @@ class ReportPDFCreator : ChangeNotifier // start making PDF! var outputFileName = reportTitle + ".pdf"; var convertedDir = Utilities.GetTempConvertedImagesFolderPath(); - var folderName = new DirectoryInfo(workingDirPath).Name; + var folderName = new DirectoryInfo(outputFolderPath).Name; if (folderName.Contains('-')) { // see if year/month format @@ -170,7 +170,7 @@ class ReportPDFCreator : ChangeNotifier var pdfRenderer = new PdfDocumentRenderer { Document = pdfDoc, - WorkingDirectory = workingDirPath + WorkingDirectory = outputFolderPath }; var hasAddedData = false; for (var i = 0; i < reportFiles.Count; i++) diff --git a/src/MayShow.Shared/ViewModels/CreatePDFReportViewModel.cs b/src/MayShow.Shared/ViewModels/CreatePDFReportViewModel.cs index ff9e016..574aae8 100644 --- a/src/MayShow.Shared/ViewModels/CreatePDFReportViewModel.cs +++ b/src/MayShow.Shared/ViewModels/CreatePDFReportViewModel.cs @@ -46,23 +46,6 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger InitializeProgramLog(); } - // this is the "normal path" into the pdf report view - // pathToLoad is presumably _settings.LastUsedPath but doesn't have to be - // public CreatePDFReportViewModel(string pathToLoad, IChangeViewModel viewModelChanger) : this(viewModelChanger) - // { - // _isPerformingInitialLoad = true; - // if (!string.IsNullOrWhiteSpace(pathToLoad)) - // { - // LogInfo("Loading report data at path: {0}", pathToLoad); - // ScanFolder(pathToLoad); - // } - // else - // { - // LogInfo("Choose a receipt folder to begin..."); - // } - // _isPerformingInitialLoad = false; - // } - public CreatePDFReportViewModel(PDFReportInfo reportInfo, IChangeViewModel viewModelChanger) : this(viewModelChanger) { _isPerformingInitialLoad = true; @@ -75,15 +58,15 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger } else { - // load data file in internal dir + UUID - var path = Path.Combine(Utilities.GetInternalDataPath(), _pdfReport.UUID); - if (Directory.Exists(path)) + // load data file in internal data report dir + _pdfReport.BaseFolder = Path.Combine(Utilities.GetInternalDataPath(), _pdfReport.UUID); + if (Directory.Exists(_pdfReport.BaseFolder)) { - ScanFolder(path); // even if points entirely to internal folder, we will be A-OK loading here + ScanFolder(_pdfReport.BaseFolder); // even if points entirely to internal folder, we will be A-OK loading here } else { - LogInfo("Erorr loading report! Folder does not exist: {0}", path); + LogInfo("Erorr loading report! Folder does not exist: {0}", _pdfReport.BaseFolder); } } _isPerformingInitialLoad = false; @@ -99,7 +82,6 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger _pdfReport = value; NotifyPropertyChanged(nameof(ReportTitle)); NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); - NotifyPropertyChanged(nameof(WorkingFolder)); NotifyPropertyChanged(nameof(ReportFiles)); SetupFileCollectionChangedWatcher(); } @@ -112,20 +94,14 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger { _pdfReport.Title = value; NotifyPropertyChanged(); - NotifyPropertyChanged(nameof(IsTitleBoxVisible)); NotifyPropertyChanged(nameof(CanAddItem)); HasUnsavedWork = true; } } - public bool IsTitleBoxVisible - { - get => !string.IsNullOrWhiteSpace(WorkingFolder); - } - public bool CanAddItem { - get => IsTitleBoxVisible && !IsCreatingPDF; + get => !IsCreatingPDF; } public bool IsCreatingPDF @@ -136,7 +112,6 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger _isCreatingPDF = value; NotifyPropertyChanged(); NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); - NotifyPropertyChanged(nameof(HasWorkingFolderAndNotMakingPDF)); NotifyPropertyChanged(nameof(CanAddItem)); } } @@ -146,38 +121,6 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger get => !_isCreatingPDF && _pdfReport.Files.Count > 0; } - public bool HasWorkingFolder - { - get => !string.IsNullOrWhiteSpace(WorkingFolder) && Directory.Exists(WorkingFolder); - } - - public bool HasWorkingFolderAndNotMakingPDF - { - get => !string.IsNullOrWhiteSpace(WorkingFolder) && Directory.Exists(WorkingFolder) && !_isCreatingPDF; - } - - public string WorkingFolder - { - get - { - if (string.IsNullOrWhiteSpace(_pdfReport.BaseFolder)) - { - return Path.Combine(Utilities.GetInternalDataPath(), _pdfReport.UUID); - } - else - { - return _pdfReport.BaseFolder; - } - } - set - { - _pdfReport.BaseFolder = value; - NotifyPropertyChanged(); - NotifyPropertyChanged(nameof(HasWorkingFolder)); - NotifyPropertyChanged(nameof(HasWorkingFolderAndNotMakingPDF)); - } - } - public string ProgramLog { get => _programLog; @@ -269,25 +212,20 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger } } - private string GetReportSavedDataPath(string workingFolder) + private string GetReportSavedDataPath() { - var internalPath = Utilities.GetInternalDataPath(); - var internalReportDataDir = Path.Combine(internalPath, _pdfReport.UUID); - if (!Directory.Exists(internalReportDataDir)) + if (!Directory.Exists(_pdfReport.BaseFolder)) { - Directory.CreateDirectory(internalReportDataDir); + Directory.CreateDirectory(_pdfReport.BaseFolder); } - return Path.Combine(internalReportDataDir, Constants.ReportSavedDataFileName); + return Path.Combine(_pdfReport.BaseFolder, Constants.ReportSavedDataFileName); } private void ScanFolder(string path) { if (Directory.Exists(path)) { - WorkingFolder = path; - NotifyPropertyChanged(nameof(IsTitleBoxVisible)); - NotifyPropertyChanged(nameof(CanAddItem)); - var reportFilePath = GetReportSavedDataPath(path); + var reportFilePath = GetReportSavedDataPath(); var successfullyLoadedPriorReportFile = false; if (File.Exists(reportFilePath)) { @@ -307,19 +245,19 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger // Scan folder for files and display in DataGrid if (path != PDFReport.BaseFolder) { - // in this case, there is essentially no report existing, + // in this case, there is essentially no existing report, // so we need to make a new one. PDFReport = new PDFReport() { Title = Path.GetDirectoryName(path) ?? "", LastSaved = null, UUID = Utilities.GetUniqueReportGuid(_settings).ToString(), - BaseFolder = path }; + PDFReport.UpdateBaseFolder(); } ReportFiles.Clear(); ReportTitle = ""; - var filePaths = Directory.GetFiles(WorkingFolder); + var filePaths = Directory.GetFiles(path); foreach (var filePath in filePaths) { AddFileBasedOnPath(filePath); @@ -539,7 +477,7 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger { try { - await Task.Run(() => CreatePDF(WorkingFolder)); + await Task.Run(() => CreatePDF(outputFolderPath: _pdfReport.BaseFolder)); } catch (Exception e) { LogInfo("PDF process failed! Reason: " + e.Message); @@ -578,14 +516,8 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger private async Task SavePDFReportDataToDisk(PDFReport report) { - var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions()); - using var memoryStream = new MemoryStream(); - await JsonSerializer.SerializeAsync(memoryStream, report, jsonContext.PDFReport); - memoryStream.Position = 0; - using var reader = new StreamReader(memoryStream); - var json = await reader.ReadToEndAsync(); - var savePath = GetReportSavedDataPath(WorkingFolder); - await File.WriteAllTextAsync(savePath, json); + var savePath = GetReportSavedDataPath(); + await Utilities.SaveReportDataAsync(report, savePath); LogInfo("Saved report information to {0}", savePath); HasUnsavedWork = false; UpdateRecentlyUsed?.UpdateRecentlyUsed(report); @@ -602,11 +534,11 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger } } - private async Task CreatePDF(string folderPath) + private async Task CreatePDF(string outputFolderPath) { IsCreatingPDF = true; var reportCreator = new ReportPDFCreator(this); - var outputPdfFile = await reportCreator.CreatePDF(ReportFiles.ToList(), ReportTitle, folderPath, new PDFFontResolver(_processDir, this), _settings); + var outputPdfFile = await reportCreator.CreatePDF(ReportFiles.ToList(), ReportTitle, outputFolderPath, new PDFFontResolver(_processDir, this), _settings); if (!string.IsNullOrWhiteSpace(outputPdfFile)) { await CreateAndSaveReportObjectAfterReportCreation(); @@ -617,8 +549,7 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger public async void ReturnToMainMenu() { - bool isSafeToReturn = await CheckIsSafeToShutdown(); - if (isSafeToReturn) + if (await CheckIsSafeToShutdown()) { PopViewModel(); } @@ -626,7 +557,7 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger public async Task CheckIsSafeToShutdown() { - if (!HasUnsavedWork || string.IsNullOrWhiteSpace(WorkingFolder)) + if (!HasUnsavedWork) { return true; } diff --git a/src/MayShow.Shared/ViewModels/SettingsViewModel.cs b/src/MayShow.Shared/ViewModels/SettingsViewModel.cs index 2cea03d..30a6f19 100644 --- a/src/MayShow.Shared/ViewModels/SettingsViewModel.cs +++ b/src/MayShow.Shared/ViewModels/SettingsViewModel.cs @@ -1,20 +1,9 @@ #nullable enable using System; -using System.Collections.ObjectModel; -using System.Globalization; using System.IO; -using System.Linq; -using System.Threading.Tasks; using Avalonia.Platform.Storage; -using Avalonia.Themes.Fluent; 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.Models; using MayShow.Helpers; diff --git a/src/MayShow.Shared/ViewModels/StartNewChooseReportViewModel.cs b/src/MayShow.Shared/ViewModels/StartNewChooseReportViewModel.cs index ea7d364..c820625 100644 --- a/src/MayShow.Shared/ViewModels/StartNewChooseReportViewModel.cs +++ b/src/MayShow.Shared/ViewModels/StartNewChooseReportViewModel.cs @@ -1,15 +1,12 @@ #nullable enable using System.Collections.ObjectModel; -using System.IO; using System.Linq; using DialogHostAvalonia; using MayShow.Interfaces; using MayShow.Models; using MayShow.Helpers; -using System; using System.Threading.Tasks; -using Avalonia.Platform.Storage; namespace MayShow.ViewModels; @@ -56,7 +53,7 @@ class StartNewChooseReportViewModel : BaseViewModel, ICanCheckShutdown, IUpdateR LastSaved = null, UUID = Utilities.GetUniqueReportGuid(_settings).ToString() }; - reportInfo.BaseFolder = Path.Combine(Utilities.GetInternalDataPath(), reportInfo.UUID); // default to internal directory + reportInfo.UpdateBaseFolder(); // now update UI ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(reportInfo, ViewModelChanger) { @@ -66,37 +63,6 @@ class StartNewChooseReportViewModel : BaseViewModel, ICanCheckShutdown, IUpdateR CreatingReportTitle = ""; // when user comes back they can start another new report } - public async void StartReportFromFolder() - { - // pick folder, then create new report based on folder - // use folder name as report title for now - var topLevel = TopLevelGrabber?.GetTopLevel(); - if (topLevel is not null) - { - var folders = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions() - { - Title = "Pick a folder of files...", - AllowMultiple = false, - }); - if (folders.Count == 1) - { - var folder = folders[0]; - var reportInfo = new PDFReportInfo() - { - Title = Path.GetDirectoryName(folder.Path.LocalPath) ?? "", - LastSaved = null, - UUID = Utilities.GetUniqueReportGuid(_settings).ToString(), - BaseFolder = folder.Path.LocalPath - }; - ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(reportInfo, ViewModelChanger) - { - UpdateRecentlyUsed = this, - TopLevelGrabber = TopLevelGrabber - }); - } - } - } - public void LoadExistingReport(object info) => LoadExistingReportImpl((PDFReportInfo) info); public void LoadExistingReportImpl(PDFReportInfo reportInfo) { diff --git a/src/MayShow.Shared/Views/CreatePDFReportView.axaml b/src/MayShow.Shared/Views/CreatePDFReportView.axaml index a1c4e40..5140e75 100644 --- a/src/MayShow.Shared/Views/CreatePDFReportView.axaml +++ b/src/MayShow.Shared/Views/CreatePDFReportView.axaml @@ -25,23 +25,8 @@ Margin="4,4,0,4"> Return to Main Menu - - diff --git a/src/MayShow.Shared/Views/StartNewChooseReport.axaml b/src/MayShow.Shared/Views/StartNewChooseReport.axaml index a4501ec..27aa8fc 100644 --- a/src/MayShow.Shared/Views/StartNewChooseReport.axaml +++ b/src/MayShow.Shared/Views/StartNewChooseReport.axaml @@ -11,7 +11,7 @@ xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia" x:DataType="vm:StartNewChooseReportViewModel"> + RowDefinitions="Auto, Auto, Auto, Auto, Auto, *"> -