WIP: Add iOS version #10

Draft
Deadpikle wants to merge 67 commits from feature/ios into main
3 changed files with 139 additions and 91 deletions
Showing only changes of commit 9391e868a2 - Show all commits
@@ -19,27 +19,22 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
{ {
private bool _isPerformingInitialLoad; private bool _isPerformingInitialLoad;
private string _processDir; private string _processDir;
private string _programLog;
private bool _isCreatingPDF; private bool _isCreatingPDF;
private string _programLog = "";
private string _workingFolder;
private string _reportTitle; private PDFReport _pdfReport;
private ObservableCollection<ReportFile> _reportFiles;
private DateTime? _lastGeneratedTime;
private Settings _settings; private Settings _settings;
private bool _hasUnsavedWork; private bool _hasUnsavedWork;
private CreatePDFReportViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger) private CreatePDFReportViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger)
{ {
_pdfReport = new PDFReport();
_processDir = Path.GetDirectoryName(Environment.ProcessPath) ?? ""; _processDir = Path.GetDirectoryName(Environment.ProcessPath) ?? "";
Console.WriteLine("Internal storage directory is: {0}", Utilities.GetInternalDataPath()); Console.WriteLine("Internal storage directory is: {0}", Utilities.GetInternalDataPath());
_isCreatingPDF = false; _isCreatingPDF = false;
_workingFolder = ""; ReportFiles = [];
ReportFiles = _reportFiles = new ObservableCollection<ReportFile>(); _programLog = "";
_reportTitle = "";
_lastGeneratedTime = null;
_settings = Settings.LoadSettings(); // TODO: needs tweaking _settings = Settings.LoadSettings(); // TODO: needs tweaking
HasUnsavedWork = false; HasUnsavedWork = false;
// setup initial quote and program log data // setup initial quote and program log data
@@ -91,24 +86,12 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
_isPerformingInitialLoad = false; _isPerformingInitialLoad = false;
} }
private void InitializeProgramLog()
{
var quotes = Constants.GetQuotes();
var random = new Random();
var quoteIndex = random.Next(0, quotes.Length);
_programLog = "----- MayShow v" + Constants.AppVersion + " ------" + Environment.NewLine;
_programLog += quotes[quoteIndex] + Environment.NewLine;
_programLog += "---------------------------------------" + Environment.NewLine;
_programLog += "Loaded and ready to create report!" + Environment.NewLine;
_programLog += "Please copy and send this Program Log when reporting any issues with the software.";
}
public string ReportTitle public string ReportTitle
{ {
get => _reportTitle; get => _pdfReport.Title;
set set
{ {
_reportTitle = value; _pdfReport.Title = value;
NotifyPropertyChanged(); NotifyPropertyChanged();
NotifyPropertyChanged(nameof(IsTitleBoxVisible)); NotifyPropertyChanged(nameof(IsTitleBoxVisible));
NotifyPropertyChanged(nameof(CanAddItem)); NotifyPropertyChanged(nameof(CanAddItem));
@@ -140,7 +123,7 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
public bool IsCreatePDFButtonEnabled public bool IsCreatePDFButtonEnabled
{ {
get => !_isCreatingPDF && _reportFiles.Count > 0; get => !_isCreatingPDF && _pdfReport.Files.Count > 0;
} }
public bool HasWorkingFolder public bool HasWorkingFolder
@@ -155,10 +138,20 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
public string WorkingFolder public string WorkingFolder
{ {
get => _workingFolder; get
{
if (string.IsNullOrWhiteSpace(_pdfReport.BaseFolder))
{
return Path.Combine(Utilities.GetInternalDataPath(), _pdfReport.UUID);
}
else
{
return _pdfReport.BaseFolder;
}
}
set set
{ {
_workingFolder = value; _pdfReport.BaseFolder = value;
NotifyPropertyChanged(); NotifyPropertyChanged();
NotifyPropertyChanged(nameof(HasWorkingFolder)); NotifyPropertyChanged(nameof(HasWorkingFolder));
NotifyPropertyChanged(nameof(HasWorkingFolderAndNotMakingPDF)); NotifyPropertyChanged(nameof(HasWorkingFolderAndNotMakingPDF));
@@ -183,12 +176,12 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
public ObservableCollection<ReportFile> ReportFiles public ObservableCollection<ReportFile> ReportFiles
{ {
get => _reportFiles; get => _pdfReport.Files;
set set
{ {
_reportFiles = value; _pdfReport.Files = value;
NotifyPropertyChanged(); NotifyPropertyChanged();
_reportFiles.CollectionChanged += ( sender, e ) => _pdfReport.Files.CollectionChanged += ( sender, e ) =>
{ {
NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled));
HasUnsavedWork = true; HasUnsavedWork = true;
@@ -196,6 +189,18 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
} }
} }
private void InitializeProgramLog()
{
var quotes = Constants.GetQuotes();
var random = new Random();
var quoteIndex = random.Next(0, quotes.Length);
_programLog = "----- MayShow v" + Constants.AppVersion + " ------" + Environment.NewLine;
_programLog += quotes[quoteIndex] + Environment.NewLine;
_programLog += "---------------------------------------" + Environment.NewLine;
_programLog += "Loaded and ready to create report!" + Environment.NewLine;
_programLog += "Please copy and send this Program Log when reporting any issues with the software.";
}
public void LogInfo(string message, params object[]? arguments) public void LogInfo(string message, params object[]? arguments)
{ {
var timestamp = string.Format("[{0:s}]", DateTime.Now); var timestamp = string.Format("[{0:s}]", DateTime.Now);
@@ -227,12 +232,41 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
} }
} }
private string GetReportSavedDataPath(string folderPath) private string GetReportSavedDataPath(string workingFolder)
{ {
if (_settings.SaveReportJsonDataInInternalDir) if (_settings.SaveReportJsonDataInInternalDir)
{ {
// TODO: keep thinking through how this is going to work and sync back
// to the main new/load screen and all that.
// cause at this point there may already be a dir...depending on flow...
// .......
// basically we need to decide when report data is first saved to disk,
// when a unique UUID is chosen, and how to sync data back to the overall
// settings object so the main menu continues to function.
// and need to get rid of all use of WorkingFolderToInternalFolderName.
// and fix/finish up fixing constructor(s).
// .......
// some tmp code follows
var internalPath = Utilities.GetInternalDataPath(); var internalPath = Utilities.GetInternalDataPath();
if (!_settings.WorkingFolderToInternalFolderName.ContainsKey(folderPath)) var didFind = false;
foreach (var report in _settings.AllReportInfo)
{
if (report.UUID == _pdfReport.UUID)
{
didFind = true;
break;
}
}
if (!didFind)
{
var reportPath = Path.Combine(internalPath, _pdfReport.UUID);
Directory.CreateDirectory(reportPath);
}
// if we already don't know about this working folder, make sure we have a folder for its files.
if (!_settings.WorkingFolderToInternalFolderName.ContainsKey(workingFolder))
{ {
var uuid = ""; var uuid = "";
var potentialPath = ""; var potentialPath = "";
@@ -247,18 +281,18 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
// make internal dir -- using dir so we have option to copy data into dir later if needed // make internal dir -- using dir so we have option to copy data into dir later if needed
// (if we ever implement a more robust report system where we keep all files) // (if we ever implement a more robust report system where we keep all files)
Directory.CreateDirectory(potentialPath); Directory.CreateDirectory(potentialPath);
_settings.WorkingFolderToInternalFolderName[folderPath] = uuid; _settings.WorkingFolderToInternalFolderName[workingFolder] = uuid;
_settings.SaveSettingsNotAsync(); // save new key/value pair _settings.SaveSettingsNotAsync(); // save new key/value pair
} }
return Path.Combine( return Path.Combine(
internalPath, internalPath,
_settings.WorkingFolderToInternalFolderName[folderPath], _settings.WorkingFolderToInternalFolderName[workingFolder],
Constants.ReportSavedDataFileName Constants.ReportSavedDataFileName
); );
} }
else else
{ {
return Path.Combine(folderPath, Constants.ReportSavedDataFileName); return Path.Combine(workingFolder, Constants.ReportSavedDataFileName);
} }
} }
@@ -283,8 +317,8 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
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; _pdfReport.LastGenerated = report.LastGenerated ?? null;
LogInfo("Reloaded report last saved at {0}", report.LastSaved); LogInfo("Reloaded report last saved at {0}", report.LastSaved ?? DateTime.Now);
successfullyLoadedPriorReport = true; successfullyLoadedPriorReport = true;
} }
} }
@@ -555,15 +589,15 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
public async Task SaveInterimReportInfo() public async Task SaveInterimReportInfo()
{ {
var report = new PDFReport() _pdfReport.LastSaved = DateTime.Now;
{ await SavePDFReportDataToDisk(_pdfReport);
Title = ReportTitle, }
Files = ReportFiles.ToList(),
BaseFolder = WorkingFolder, private async Task CreateAndSaveReportObjectAfterReportCreation()
LastSaved = DateTime.Now, {
LastGenerated = _lastGeneratedTime, _pdfReport.LastSaved = DateTime.Now;
}; _pdfReport.LastGenerated = DateTime.Now;
await SavePDFReportDataToDisk(report); await SavePDFReportDataToDisk(_pdfReport);
} }
private async Task SavePDFReportDataToDisk(PDFReport report) private async Task SavePDFReportDataToDisk(PDFReport report)
@@ -580,20 +614,6 @@ class CreatePDFReportViewModel : BaseViewModel, ICanCheckShutdown, ILogger
HasUnsavedWork = false; HasUnsavedWork = false;
} }
private async Task CreateAndSaveReportObjectAfterReportCreation()
{
var report = new PDFReport()
{
Title = ReportTitle,
Files = ReportFiles.ToList(),
BaseFolder = WorkingFolder,
LastSaved = DateTime.Now,
LastGenerated = DateTime.Now,
};
_lastGeneratedTime = DateTime.Now;
await SavePDFReportDataToDisk(report);
}
// called from UI button // called from UI button
public async Task CopyLogToClipboard() public async Task CopyLogToClipboard()
{ {
@@ -10,6 +10,7 @@ using MayShow.Helpers;
using MayShows.Helpers; using MayShows.Helpers;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Platform.Storage;
namespace MayShow.ViewModels; namespace MayShow.ViewModels;
@@ -43,48 +44,60 @@ class StartNewChooseReportViewModel : BaseViewModel, ICanCheckShutdown
set { _savedReports = value; NotifyPropertyChanged(); } set { _savedReports = value; NotifyPropertyChanged(); }
} }
public async void StartReport() public async void StartReport() // start a new report based on a title alone
{ {
if (string.IsNullOrWhiteSpace(CreatingReportTitle)) if (string.IsNullOrWhiteSpace(CreatingReportTitle))
{ {
await DialogHost.Show(new WarningViewModel("Report title cannot be blank!")); await DialogHost.Show(new WarningViewModel("Report title cannot be blank!"));
return; return;
} }
// TODO: if report with name already exists in system, error
var reportInfo = new PDFReportInfo() var reportInfo = new PDFReportInfo()
{ {
Title = CreatingReportTitle, Title = CreatingReportTitle,
LastSaved = DateTime.Now LastSaved = null,
UUID = Utilities.GetUniqueReportGuid(_settings).ToString()
}; };
// create folder for report data reportInfo.BaseFolder = Path.Combine(Utilities.GetInternalDataPath(), reportInfo.UUID); // default to internal directory
var path = Path.Combine(Utilities.GetInternalDataPath(), reportInfo.UUID);
while (Directory.Exists(path))
{
reportInfo.ResetUUID();
path = Path.Combine(Utilities.GetInternalDataPath(), reportInfo.UUID);
}
Directory.CreateDirectory(path);
reportInfo.BaseFolder = path; // default to internal directory
_settings.AllReportInfo.Add(reportInfo);
// ... this sort and save is slow, technically, but we're not going to have millions of items here, so... // ... this sort and save 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)); // TODO: save automatically only if mobile; desktop only saves on command
await _settings.SaveSettingsAsync(); // SavedReports = new ObservableCollection<PDFReportInfo>(_settings.AllReportInfo.OrderBy(x => x.Title));
// await _settings.SaveSettingsAsync();
// now update UI // now update UI
ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(reportInfo, ViewModelChanger) ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(reportInfo, ViewModelChanger));
{
ReportTitle = CreatingReportTitle
});
CreatingReportTitle = ""; // when user comes back they can start another new report 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));
}
}
}
public void LoadExistingReport(object info) => LoadExistingReportImpl((PDFReportInfo) info); public void LoadExistingReport(object info) => LoadExistingReportImpl((PDFReportInfo) info);
public void LoadExistingReportImpl(PDFReportInfo reportInfo) public void LoadExistingReportImpl(PDFReportInfo reportInfo)
{ {
// TODO: load data and send to create PDF report view model ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(reportInfo, ViewModelChanger));
ViewModelChanger.PushViewModel(new CreatePDFReportViewModel(reportInfo, ViewModelChanger)
{
ReportTitle = CreatingReportTitle
});
} }
public void DeleteExistingReport(object info) => DeleteExistingReportImpl((PDFReportInfo) info); public void DeleteExistingReport(object info) => DeleteExistingReportImpl((PDFReportInfo) info);
@@ -11,7 +11,7 @@
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia" xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
x:DataType="vm:StartNewChooseReportViewModel"> x:DataType="vm:StartNewChooseReportViewModel">
<Grid ColumnDefinitions="100, *, 100" <Grid ColumnDefinitions="100, *, 100"
RowDefinitions="Auto, Auto, Auto, Auto, Auto, *"> RowDefinitions="Auto, Auto, Auto, Auto, Auto, Auto, Auto, *">
<TextBlock HorizontalAlignment="Center" <TextBlock HorizontalAlignment="Center"
FontSize="36" FontSize="36"
FontWeight="Bold" FontWeight="Bold"
@@ -28,7 +28,7 @@
Grid.Row="1" /> Grid.Row="1" />
<Label Content="Start New Report" <Label Content="Start New Report"
HorizontalAlignment="Left" HorizontalAlignment="Left"
FontSize="18" FontSize="16"
FontWeight="Bold" FontWeight="Bold"
Margin="0,24,0,0" Margin="0,24,0,0"
Grid.Column="1" Grid.Column="1"
@@ -51,19 +51,34 @@
Grid.Column="1"> Grid.Column="1">
<TextBlock> <TextBlock>
<Run Text="&#xe494;" <Run Text="&#xe494;"
FontFamily="{StaticResource FontAwesomeSolid}" /> Create and Start Report</TextBlock> FontFamily="{StaticResource FontAwesomeSolid}" /> Create Blank Report</TextBlock>
</Button> </Button>
</Grid> </Grid>
<Label Content="Load Existing Report" <Label Content="Start New Report From Existing Files"
FontSize="18" HorizontalAlignment="Left"
FontSize="16"
FontWeight="Bold"
Margin="0,8,0,0"
Grid.Column="1"
Grid.Row="4"/>
<Button Command="{Binding StartReportFromFolder}"
Classes="accent"
Grid.Row="5"
Grid.Column="1">
<TextBlock>
<Run Text="&#xe494;"
FontFamily="{StaticResource FontAwesomeSolid}" /> Create Report from Existing Folder</TextBlock>
</Button>
<Label Content="Load Previously Saved Report"
FontSize="16"
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="6"/>
<ScrollViewer Margin="0,8,0,32" <ScrollViewer Margin="0,8,0,32"
Grid.Column="1" Grid.Column="1"
Grid.Row="5" Grid.Row="7"
VerticalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"
AllowAutoHide="False" AllowAutoHide="False"
HorizontalScrollBarVisibility="Disabled"> HorizontalScrollBarVisibility="Disabled">