Tab restore
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.Json;
|
||||
using FileTime.App.Core.Models;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Services;
|
||||
using FileTime.Core.Timeline;
|
||||
using FileTime.Providers.Local;
|
||||
using InitableService;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.App.Core.Services.Persistence;
|
||||
|
||||
public class TabPersistenceService : ITabPersistenceService
|
||||
{
|
||||
private readonly IAppState _appState;
|
||||
private readonly ILogger<TabPersistenceService> _logger;
|
||||
|
||||
private class PersistenceRoot
|
||||
{
|
||||
public TabStates? TabStates { get; set; }
|
||||
}
|
||||
|
||||
private class TabStates
|
||||
{
|
||||
public List<TabState>? Tabs { get; set; }
|
||||
public int? ActiveTabNumber { get; set; }
|
||||
}
|
||||
|
||||
private class TabState
|
||||
{
|
||||
public string? Path { get; set; }
|
||||
public int Number { get; set; }
|
||||
|
||||
public TabState()
|
||||
{
|
||||
}
|
||||
|
||||
public TabState(FullName path, int number)
|
||||
{
|
||||
Path = path.Path;
|
||||
Number = number;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string _settingsPath;
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILocalContentProvider _localContentProvider;
|
||||
|
||||
public TabPersistenceService(
|
||||
IApplicationSettings applicationSettings,
|
||||
IAppState appState,
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
IServiceProvider serviceProvider,
|
||||
ILocalContentProvider localContentProvider,
|
||||
ILogger<TabPersistenceService> logger)
|
||||
{
|
||||
_appState = appState;
|
||||
_logger = logger;
|
||||
_settingsPath = Path.Combine(applicationSettings.AppDataRoot, "savedState.json");
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_serviceProvider = serviceProvider;
|
||||
_localContentProvider = localContentProvider;
|
||||
|
||||
_jsonOptions = new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true
|
||||
};
|
||||
}
|
||||
|
||||
public Task ExitAsync()
|
||||
{
|
||||
SaveStates();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task LoadStatesAsync()
|
||||
{
|
||||
if (!File.Exists(_settingsPath)) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var stateReader = File.OpenRead(_settingsPath);
|
||||
var state = await JsonSerializer.DeserializeAsync<PersistenceRoot>(stateReader);
|
||||
if (state != null)
|
||||
{
|
||||
await RestoreTabs(state.TabStates);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Unknown exception while restoring app state");
|
||||
}
|
||||
}
|
||||
private async Task RestoreTabs(TabStates? tabStates)
|
||||
{
|
||||
if (tabStates == null
|
||||
|| tabStates.Tabs == null)
|
||||
{
|
||||
CreateEmptyTab();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
foreach (var tab in tabStates.Tabs)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tab.Path == null) continue;
|
||||
|
||||
IContainer? container = null;
|
||||
var path = new FullName(tab.Path);
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pathItem = await _timelessContentProvider.GetItemByFullNameAsync(path, PointInTime.Present);
|
||||
|
||||
container = pathItem switch
|
||||
{
|
||||
IContainer c => c,
|
||||
IElement e => e.Parent?.ResolveAsync() as IContainer,
|
||||
_ => null
|
||||
};
|
||||
break;
|
||||
}
|
||||
catch
|
||||
{
|
||||
path = path.GetParent();
|
||||
if (path == null)
|
||||
{
|
||||
throw new Exception($"Could not find an initializable path along {tab.Path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (container == null) continue;
|
||||
|
||||
var tabToLoad = _serviceProvider.GetInitableResolver(container)
|
||||
.GetRequiredService<ITab>();
|
||||
var tabViewModel = _serviceProvider.GetInitableResolver(tabToLoad, tab.Number).GetRequiredService<ITabViewModel>();
|
||||
|
||||
_appState.AddTab(tabViewModel);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Unkown exception while restoring tab. {TabState}", JsonSerializer.Serialize(tab, _jsonOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Unkown exception while restoring tabs.");
|
||||
}
|
||||
|
||||
if (_appState.Tabs.Count == 0)
|
||||
{
|
||||
CreateEmptyTab();
|
||||
}
|
||||
else
|
||||
{
|
||||
var tabToActivate = _appState.Tabs.FirstOrDefault(t => t.TabNumber == tabStates.ActiveTabNumber);
|
||||
if (tabToActivate is not null) _appState.SetSelectedTab(tabToActivate);
|
||||
}
|
||||
|
||||
void CreateEmptyTab()
|
||||
{
|
||||
var tab = _serviceProvider.GetInitableResolver<IContainer>(_localContentProvider)
|
||||
.GetRequiredService<ITab>();
|
||||
var tabViewModel = _serviceProvider.GetInitableResolver(tab, 1).GetRequiredService<ITabViewModel>();
|
||||
|
||||
_appState.AddTab(tabViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStates()
|
||||
{
|
||||
var state = new PersistenceRoot
|
||||
{
|
||||
TabStates = SerializeTabStates()
|
||||
};
|
||||
var settingsDirectory = new DirectoryInfo(string.Join(Path.DirectorySeparatorChar, _settingsPath.Split(Path.DirectorySeparatorChar)[0..^1]));
|
||||
if (!settingsDirectory.Exists) settingsDirectory.Create();
|
||||
var serializedData = JsonSerializer.Serialize(state, _jsonOptions);
|
||||
File.WriteAllText(_settingsPath, serializedData);
|
||||
}
|
||||
|
||||
private TabStates SerializeTabStates()
|
||||
{
|
||||
var tabStates = new List<TabState>();
|
||||
foreach (var tab in _appState.Tabs)
|
||||
{
|
||||
var currentLocation = tab.CachedCurrentLocation;
|
||||
if (currentLocation is null) continue;
|
||||
tabStates.Add(new TabState(currentLocation.FullName!, tab.TabNumber));
|
||||
}
|
||||
|
||||
return new TabStates()
|
||||
{
|
||||
Tabs = tabStates,
|
||||
ActiveTabNumber = _appState.CurrentSelectedTab?.TabNumber
|
||||
};
|
||||
}
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
await LoadStatesAsync();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user