Skip to content

Commit 1046981

Browse files
authored
feat(clips): use file system to store clips in files. (#64)
1 parent 3a1b868 commit 1046981

File tree

10 files changed

+187
-93
lines changed

10 files changed

+187
-93
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"filemaker",
2525
"fmxmlsnippet",
2626
"Mvvm",
27+
"nlog",
2728
"xdoc",
2829
"XMFD",
2930
"XMSC",

App.axaml.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
using SharpFM.ViewModels;
55
using Microsoft.Extensions.Logging;
66
using NLog.Extensions.Logging;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using System;
9+
using SharpFM.Services;
710

811
namespace SharpFM;
912

@@ -24,8 +27,24 @@ public override void OnFrameworkInitializationCompleted()
2427
{
2528
DataContext = new MainWindowViewModel(logger)
2629
};
30+
31+
var services = new ServiceCollection();
32+
33+
services.AddSingleton(x => new FolderService(desktop.MainWindow));
34+
35+
Services = services.BuildServiceProvider();
2736
}
2837

2938
base.OnFrameworkInitializationCompleted();
3039
}
40+
41+
/// <summary>
42+
/// Get a reference to the current app.
43+
/// </summary>
44+
public new static App? Current => Application.Current as App;
45+
46+
/// <summary>
47+
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
48+
/// </summary>
49+
public IServiceProvider? Services { get; private set; }
3150
}

MainWindow.axaml

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,41 @@
1919
<DockPanel>
2020
<Menu DockPanel.Dock="Top">
2121
<MenuItem Header="_File">
22-
<MenuItem Command="{Binding NewEmptyItem}" Header="_New" />
22+
<MenuItem Header="_New">
23+
<MenuItem Command="{Binding NewEmptyItem}" Header="Blank Clip" />
24+
<MenuItem Command="{Binding PasteFileMakerClipData}" Header="From Clipboard (copied from FileMaker)" />
25+
</MenuItem>
2326
<Separator />
24-
<MenuItem Command="{Binding SaveToDb}" Header="Save All" />
27+
<MenuItem Command="{Binding OpenFolderPicker}" Header="Open Folder" />
28+
<Separator />
29+
<MenuItem Header="Save">
30+
<MenuItem Command="{Binding SaveClipsStorage}" Header="Save All To Folder" />
31+
<MenuItem Command="{Binding CopySelectedToClip}" Header="Selected clip to Clipboard (to paste into FileMaker)" />
32+
</MenuItem>
2533
<Separator />
2634
<MenuItem Command="{Binding ExitApplication}" Header="_Exit" />
2735
</MenuItem>
28-
<MenuItem Header="_Edit">
29-
<MenuItem Command="{Binding CopySelectedToClip}" Header="Copy as FileMaker Blob" />
30-
<MenuItem Command="{Binding PasteFileMakerClipData}" Header="Paste From FileMaker Blob" />
31-
</MenuItem>
3236
<MenuItem Header="Transform">
3337
<MenuItem Command="{Binding CopyAsClass}" Header="Copy as C# Class" />
3438
</MenuItem>
35-
<MenuItem Header="Storage">
36-
<MenuItem Command="{Binding ClearDb}" Header="Clear Db" />
37-
</MenuItem>
3839
<MenuItem Header="{Binding Version}" />
3940
</Menu>
40-
<TextBlock />
4141
<Grid>
42+
<Grid.RowDefinitions>
43+
<RowDefinition Height="40" />
44+
<RowDefinition Height="*" />
45+
</Grid.RowDefinitions>
4246
<Grid.ColumnDefinitions>
4347
<ColumnDefinition Width="225" />
4448
<ColumnDefinition Width="*" />
4549
</Grid.ColumnDefinitions>
4650

51+
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,8">
52+
<TextBox Text="{Binding CurrentPath, Mode=OneWay}" />
53+
</StackPanel>
54+
4755
<ListBox
56+
Grid.Row="1"
4857
Grid.Column="0"
4958
ItemsSource="{Binding FileMakerClips}"
5059
SelectedItem="{Binding SelectedClip}">
@@ -65,6 +74,7 @@
6574

6675
<AvaloniaEdit:TextEditor
6776
x:Name="avaloniaEditor"
77+
Grid.Row="1"
6878
Grid.Column="1"
6979
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"
7080
ShowLineNumbers="True"

Models/Clip.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ namespace SharpFM.Models;
55
/// </summary>
66
public class Clip
77
{
8-
/// <summary>
9-
/// Database Id
10-
/// </summary>
11-
public int ClipId { get; set; }
12-
138
/// <summary>
149
/// Display name for clip may match Name inside the xml data or may not.
1510
/// </summary>

Models/ClipDbContext.cs

Lines changed: 0 additions & 36 deletions
This file was deleted.

Models/ClipRepository.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
5+
namespace SharpFM.Models;
6+
7+
/// <summary>
8+
/// Clip File Repository.
9+
/// </summary>
10+
public class ClipRepository
11+
{
12+
/// <summary>
13+
/// Clips stored in the specified folder.
14+
/// </summary>
15+
public ICollection<Clip> Clips { get; init; }
16+
17+
/// <summary>
18+
/// Database path.
19+
/// </summary>
20+
public string ClipPath { get; }
21+
22+
/// <summary>
23+
/// Constructor.
24+
/// </summary>
25+
public ClipRepository(string path)
26+
{
27+
// ensure the directory exists
28+
if (!Directory.Exists(path))
29+
{
30+
Directory.CreateDirectory(path);
31+
}
32+
33+
ClipPath = path;
34+
35+
// init clips to empty
36+
Clips = [];
37+
}
38+
39+
/// <summary>
40+
/// Load clips from the path specified by <see cref="ClipPath"/>.
41+
/// </summary>
42+
public void LoadClips()
43+
{
44+
foreach (var clipFile in Directory.EnumerateFiles(ClipPath))
45+
{
46+
var fi = new FileInfo(clipFile);
47+
48+
var clip = new Clip
49+
{
50+
ClipName = fi.Name.Replace(fi.Extension, string.Empty),
51+
ClipType = fi.Extension.Replace(".", string.Empty),
52+
ClipXml = File.ReadAllText(clipFile)
53+
};
54+
55+
Clips.Add(clip);
56+
}
57+
}
58+
59+
/// <summary>
60+
/// Write all clips to their associated clip type files in the path specified by <see cref="ClipPath"/>.
61+
/// </summary>
62+
public void SaveChanges()
63+
{
64+
foreach (var clip in Clips)
65+
{
66+
var clipPath = Path.Combine(ClipPath, $"{clip.ClipName}.{clip.ClipType}");
67+
68+
File.WriteAllText(clipPath, clip.ClipXml);
69+
}
70+
}
71+
}

Services/FolderService.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Avalonia.Controls;
5+
using Avalonia.Platform.Storage;
6+
7+
namespace SharpFM.Services;
8+
9+
public class FolderService(Window target)
10+
{
11+
private readonly Window _target = target;
12+
13+
public async Task<string> GetFolderAsync()
14+
{
15+
// Get top level from the current control. Alternatively, you can use Window reference instead.
16+
var topLevel = TopLevel.GetTopLevel(_target) ?? throw new ArgumentNullException(nameof(_target), "Window Target.");
17+
18+
// Start async operation to open the dialog.
19+
var folders = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
20+
{
21+
AllowMultiple = false,
22+
Title = "Select a Folder",
23+
});
24+
25+
var folder = folders.SingleOrDefault();
26+
27+
return folder?.TryGetLocalPath() ?? throw new ArgumentException("Could not load local path.");
28+
}
29+
}

SharpFM.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.9" />
3838
<PackageReference Include="FluentAvaloniaUI" Version="2.0.5" />
3939

40-
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
41-
4240
<PackageReference Include="MinVer" Version="5.0.0">
4341
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
4442
<PrivateAssets>all</PrivateAssets>

ViewModels/ClipViewModel.cs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,9 @@ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
1414

1515
public FileMakerClip Clip { get; set; }
1616

17-
public ClipViewModel(FileMakerClip clip) : this(clip, null) { }
18-
19-
public ClipViewModel(FileMakerClip clip, int? clipId)
17+
public ClipViewModel(FileMakerClip clip)
2018
{
2119
Clip = clip;
22-
ClipId = clipId;
23-
}
24-
25-
private int? _clipId;
26-
public int? ClipId
27-
{
28-
get => _clipId;
29-
set
30-
{
31-
_clipId = value;
32-
NotifyPropertyChanged();
33-
}
3420
}
3521

3622
public string ClipType

0 commit comments

Comments
 (0)