Skip to content

Commit

Permalink
Fix #14; #2 - Recent solutions
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitryGolubenkov committed Jan 15, 2023
1 parent b35e2a0 commit 17802ae
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using SharpDockerizer.AppLayer.Models;

namespace SharpDockerizer.AppLayer.Contracts;
public interface IRecentlyOpenedSolutionsService
{
/// <summary>
/// Returns a list of recently opened solutions.
/// </summary>
List<RecentlyOpenedSolution> GetSolutions();
/// <summary>
/// Adds a solution to recently opened solutions, or moves it to top of the list.
/// </summary>
Task Add(RecentlyOpenedSolution solution);
/// <summary>
/// Removes a solution from recently opened solutions.
/// </summary>
Task RemoveWithPath(string path);
}
12 changes: 12 additions & 0 deletions src/SharpDockerizer.AppLayer/Models/RecentlyOpenedSolution.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace SharpDockerizer.AppLayer.Models;
public class RecentlyOpenedSolution
{
/// <summary>
/// Solution name
/// </summary>
public required string Name { get; set; }
/// <summary>
/// Path to solution .sln file
/// </summary>
public required string AbsolutePath { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using SharpDockerizer.AppLayer.Contracts;
using SharpDockerizer.AppLayer.Models;
using System.Text;

namespace SharpDockerizer.AppLayer.Services.Solution;
public class RecentlyOpenedSolutionsService : IRecentlyOpenedSolutionsService
{

private const string saveFileName = "recentsolutions";
private string SaveFilePath { get => Path.Combine(AppContext.BaseDirectory, saveFileName); }
public List<RecentlyOpenedSolution> GetSolutions()
{
List<RecentlyOpenedSolution> result = new List<RecentlyOpenedSolution>();
// If doesn't exist - then return empty list.
if (!File.Exists(SaveFilePath))
{
return result;
}

// Read file and parse it
try
{
var solutions = File.ReadAllLines(SaveFilePath);
foreach (var solution in solutions)
{
var parts = solution.Split(';');
result.Add(new RecentlyOpenedSolution()
{
Name = $"{parts[0]} ({parts[1]})",
AbsolutePath = parts[1],
});
}

return result;
}
catch
{
// Cache file could be damaged. Remove it as we don't know what is really broken in it.
File.Delete(SaveFilePath);
return new List<RecentlyOpenedSolution>();
}
}


public async Task Add(RecentlyOpenedSolution solution)
{
// First we get what is already saved on disk
var recentSolutions = GetSolutions();

// If this solution was already opened - remove it from list
var maybeAddedIndex = recentSolutions.FindIndex(x => x.AbsolutePath == solution.AbsolutePath);
if (maybeAddedIndex != -1)
recentSolutions.RemoveAt(maybeAddedIndex);

// Insert new solution at start of the list and save
recentSolutions.Insert(0, solution);
await Save(recentSolutions);
}

public async Task RemoveWithPath(string path)
{
var recentSolutions = GetSolutions();
var maybeAddedIndex = recentSolutions.FindIndex(x => x.AbsolutePath == path);
if (maybeAddedIndex != -1)
recentSolutions.RemoveAt(maybeAddedIndex);

await Save(recentSolutions);
}

/// <summary>
/// Saves recent solutions to disk. Implemented as csv file.
/// </summary>
private async Task Save(List<RecentlyOpenedSolution> recentlyOpenedSolutions)
{
StringBuilder sb = new StringBuilder();
foreach (var solution in recentlyOpenedSolutions)
{
sb.AppendLine($"{solution.Name};{solution.AbsolutePath}");
}

await File.WriteAllTextAsync(SaveFilePath, sb.ToString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public async Task LoadSolution(string solutionFilePath)
// Create new solution data
_currentSolutionInfo.CurrentSolution = new SolutionData
{
Name = Path.GetFileNameWithoutExtension(solutionFilePath),
SolutionRootDirectoryPath = Path.GetDirectoryName(solutionFilePath),
SolutionFilePath = solutionFilePath,
};
Expand Down
1 change: 1 addition & 0 deletions src/SharpDockerizer.AvaloniaUI/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ private void ConfigureServices(IServiceCollection serviceCollection)
serviceCollection.AddTransient<IAspNetDockerImageVersionSelector, AspNetDockerImageVersionSelector>();
serviceCollection.AddTransient<IDotNetSdkImageVersionSelector, DotNetSdkImageVersionSelector>();
serviceCollection.AddTransient<IProjectDependenciesExporter, ProjectDependenciesExporter>();
serviceCollection.AddTransient<IRecentlyOpenedSolutionsService, RecentlyOpenedSolutionsService>();
}

private void ConfigureLogging(IServiceCollection services)
Expand Down
19 changes: 8 additions & 11 deletions src/SharpDockerizer.AvaloniaUI/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SharpDockerizer.AvaloniaUI.MainWindow"
Title="SharpDockerizer"
MinWidth="400"
MinHeight="250"
>
<DockPanel>
<DockPanel>
<views:TopBar DockPanel.Dock="Top" />
<SplitView IsPaneOpen="true"
DisplayMode="Inline"
OpenPaneLength="256"
DockPanel.Dock="Top"
>
<SplitView.Pane>
<views:SolutionViewer/>
</SplitView.Pane>

<views:DockerfileGenerator />
</SplitView>
<Grid ColumnDefinitions="256,2,*" DockPanel.Dock="Top">
<views:SolutionViewer Grid.Column="0" MinWidth="100" />
<GridSplitter Width="2" Grid.Column="1" />
<views:DockerfileGenerator Grid.Column="2" MinWidth="100"/>
</Grid>
</DockPanel>
</Window>
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,5 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Controls\" />
<Folder Include="Interfaces\" />
</ItemGroup>
</Project>
48 changes: 47 additions & 1 deletion src/SharpDockerizer.AvaloniaUI/ViewModels/TopBarViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using CommunityToolkit.Mvvm.Messaging;
using SharpDockerizer.AppLayer.Events;
using SharpDockerizer.AppLayer.Utilities;
using SharpDockerizer.AppLayer.Models;
using System.IO;

namespace SharpDockerizer.AvaloniaUI.ViewModels;
[INotifyPropertyChanged]
Expand All @@ -18,19 +20,40 @@ internal partial class TopBarViewModel

private ISolutionLoader _solutionLoader;
private readonly IMessenger _messenger;
private readonly IRecentlyOpenedSolutionsService _recentlyOpenedSolutionsService;
private readonly ICurrentSolutionInfo _currentSolutionInfo;

#endregion

#region Constructor

public TopBarViewModel(ISolutionLoader solutionLoader, IMessenger messenger)
public TopBarViewModel(ISolutionLoader solutionLoader, IMessenger messenger, IRecentlyOpenedSolutionsService recentlyOpenedSolutionsService, ICurrentSolutionInfo currentSolutionInfo)
{
_solutionLoader = solutionLoader;
_messenger = messenger;
_recentlyOpenedSolutionsService = recentlyOpenedSolutionsService;
_currentSolutionInfo = currentSolutionInfo;
// Load recent solutions. Also check if there are no recent solutions.

var recentSolutions = recentlyOpenedSolutionsService.GetSolutions();
if (recentSolutions is not null && recentSolutions.Count > 0)
{
RecentSolutions = recentSolutions;
RecentSolutionsEnabled = true;
}

}

#endregion

#region Observable Properties

[ObservableProperty]
private List<RecentlyOpenedSolution> _recentSolutions;
[ObservableProperty]
private bool _recentSolutionsEnabled;
#endregion

#region Relay Commands

/// <summary>
Expand Down Expand Up @@ -59,11 +82,34 @@ internal async Task LoadSolution()
{
result[0].TryGetUri(out var uri);
await _solutionLoader.LoadSolution(uri.AbsolutePath);
await _recentlyOpenedSolutionsService.Add(new RecentlyOpenedSolution()
{
Name =_currentSolutionInfo.CurrentSolution.Name,
AbsolutePath = _currentSolutionInfo.CurrentSolution.SolutionFilePath
});

RecentSolutions = _recentlyOpenedSolutionsService.GetSolutions();
RecentSolutionsEnabled = true;

_messenger.Send<SolutionLoadedEvent>();
}
}
}

[RelayCommand]
internal async Task OpenRecentSolution(object path)
{
try
{
await _solutionLoader.LoadSolution(path as string);
_messenger.Send<SolutionLoadedEvent>();
}
catch (IOException ex)
{
// TODO: Notification
_recentlyOpenedSolutionsService.RemoveWithPath(path as string);
}
}

[RelayCommand]
internal void CloseApp()
Expand Down
9 changes: 5 additions & 4 deletions src/SharpDockerizer.AvaloniaUI/Views/SolutionViewer.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
DockPanel.Dock="Right"
IsVisible="{Binding IsSolutionLoaded}"
Classes="Flat Light Topbar"
ToolTip.Tip="Refresh Solution"
>
<mati:MaterialIcon Kind="Refresh" ToolTip.Tip="Refresh Solution" />
<mati:MaterialIcon Kind="Refresh"/>
</Button>
</DockPanel>
</Border>
Expand All @@ -34,16 +35,16 @@
Margin="0,16"
>Load a .NET solution to view included projects.</TextBlock>
<ScrollViewer DockPanel.Dock="Top"
Margin="10"
Margin="0,10"
IsVisible="{Binding IsSolutionLoaded}"
VerticalAlignment="Stretch">
<ListBox Items="{Binding SolutionProjects}" SelectedItem="{Binding SelectedProject}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<StackPanel Orientation="Horizontal">
<mati:MaterialIcon Kind="ApplicationBracesOutline" />
<TextBlock Margin="8,0,0,0" Text="{Binding Path=ProjectName}"/>
</WrapPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Expand Down
10 changes: 10 additions & 0 deletions src/SharpDockerizer.AvaloniaUI/Views/TopBar.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@
</WrapPanel>
</MenuItem.Header>
<MenuItem Command="{Binding LoadSolution}" Header="_Open Solution"/>
<MenuItem IsEnabled="{Binding RecentSolutionsEnabled}" Header="_Recently Opened" Items="{Binding RecentSolutions}">
<MenuItem.Styles>
<Style Selector="MenuItem">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Command" Value="{Binding $parent[UserControl].DataContext.OpenRecentSolution}" />
<Setter Property="CommandParameter" Value="{Binding AbsolutePath}" />
</Style>
</MenuItem.Styles>

</MenuItem>
<Separator/>
<MenuItem Command="{Binding CloseApp}" Header="_Exit"/>
</MenuItem>
Expand Down
4 changes: 4 additions & 0 deletions src/SharpDockerizer.Core/Models/SolutionData.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
namespace SharpDockerizer.Core.Models;
public class SolutionData
{
/// <summary>
/// Solution name
/// </summary>
public required string Name { get; set; }
/// <summary>
/// Absolute path to solution directory.
/// </summary>
Expand Down

0 comments on commit 17802ae

Please sign in to comment.