Skip to content

Commit e22b32c

Browse files
authored
feat(storage): added a SQLite persistence layer for saving clips between runs. (#5)
1 parent 8aed465 commit e22b32c

7 files changed

Lines changed: 175 additions & 11 deletions

File tree

SharpFM.App/MainWindow.axaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,20 @@
2121
<MenuItem Header="_File">
2222
<MenuItem Command="{Binding NewEmptyItem}" Header="_New" />
2323
<Separator />
24+
<MenuItem Command="{Binding SaveToDb}" Header="Save All" />
25+
<Separator />
2426
<MenuItem Command="{Binding ExitApplication}" Header="_Exit" />
2527
</MenuItem>
2628
<MenuItem Header="_Edit">
27-
<MenuItem Command="{Binding CopySelectedToClip}" Header="Copy" />
28-
<MenuItem Command="{Binding PasteFileMakerClipData}" Header="Paste" />
29+
<MenuItem Command="{Binding CopySelectedToClip}" Header="Copy as FileMaker Blob" />
30+
<MenuItem Command="{Binding PasteFileMakerClipData}" Header="Paste From FileMaker Blob" />
2931
</MenuItem>
3032
<MenuItem Header="Transform">
3133
<MenuItem Command="{Binding CopyAsClass}" Header="Copy as C# Class" />
3234
</MenuItem>
35+
<MenuItem Header="Storage">
36+
<MenuItem Command="{Binding ClearDb}" Header="Clear Db" />
37+
</MenuItem>
3338
<MenuItem Header="{Binding Version}" />
3439
</Menu>
3540
<TextBlock />
@@ -41,7 +46,7 @@
4146

4247
<ListBox
4348
Grid.Column="0"
44-
ItemsSource="{Binding Keys}"
49+
ItemsSource="{Binding FileMakerClips}"
4550
SelectedItem="{Binding SelectedClip}">
4651
<ListBox.ItemTemplate>
4752
<DataTemplate>

SharpFM.App/Models/Clip.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace SharpFM.App.Models;
2+
3+
/// <summary>
4+
/// Clip Data Model
5+
/// </summary>
6+
public class Clip
7+
{
8+
/// <summary>
9+
/// Database Id
10+
/// </summary>
11+
public int ClipId { get; set; }
12+
13+
/// <summary>
14+
/// Display name for clip may match Name inside the xml data or may not.
15+
/// </summary>
16+
public string ClipName { get; set; } = string.Empty;
17+
18+
/// <summary>
19+
/// The data format to use when putting the data back on the clipboard for FileMaker.
20+
/// </summary>
21+
public string ClipType { get; set; } = string.Empty;
22+
23+
/// <summary>
24+
/// Raw xml data from the clip.
25+
/// </summary>
26+
public string ClipXml { get; set; } = string.Empty;
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using System;
3+
4+
5+
namespace SharpFM.App.Models;
6+
7+
/// <summary>
8+
/// Clip Db Context
9+
/// </summary>
10+
public class ClipDbContext : DbContext
11+
{
12+
/// <summary>
13+
/// Clips stored in the Db.
14+
/// </summary>
15+
public DbSet<Clip> Clips => Set<Clip>();
16+
17+
/// <summary>
18+
/// Database path.
19+
/// </summary>
20+
public string DbPath { get; }
21+
22+
/// <summary>
23+
/// Constructor.
24+
/// </summary>
25+
public ClipDbContext()
26+
{
27+
var folder = Environment.SpecialFolder.LocalApplicationData;
28+
var path = Environment.GetFolderPath(folder);
29+
DbPath = System.IO.Path.Join(path, "sharpFM.db");
30+
}
31+
32+
// The following configures EF to create a Sqlite database file in the
33+
// special "local" folder for your platform.
34+
protected override void OnConfiguring(DbContextOptionsBuilder options)
35+
=> options.UseSqlite($"Data Source={DbPath}");
36+
}

SharpFM.App/SharpFM.App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.4" />
3434
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.1" />
3535
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.1" />
36+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.11" />
3637
<PackageReference Include="MinVer" Version="4.3.0">
3738
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3839
<PrivateAssets>all</PrivateAssets>

SharpFM.App/ViewModels/ClipViewModel.cs

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

1616
public FileMakerClip Clip { get; set; }
1717

18-
public ClipViewModel(FileMakerClip clip)
18+
public ClipViewModel(FileMakerClip clip) : this(clip, null) { }
19+
20+
public ClipViewModel(FileMakerClip clip, int? clipId)
1921
{
2022
Clip = clip;
23+
ClipId = clipId;
24+
}
25+
26+
private int? _clipId;
27+
public int? ClipId
28+
{
29+
get => _clipId;
30+
set
31+
{
32+
_clipId = value;
33+
NotifyPropertyChanged();
34+
}
2135
}
2236

2337
public string ClipType

SharpFM.App/ViewModels/MainWindowViewModel.cs

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
using Avalonia;
99
using Avalonia.Controls.ApplicationLifetimes;
1010
using FluentAvalonia.UI.Data;
11+
using SharpFM.App.Models;
1112
using SharpFM.Core;
1213

1314
namespace SharpFM.App.ViewModels;
1415

1516
public partial class MainWindowViewModel : INotifyPropertyChanged
1617
{
18+
public ClipDbContext _context;
19+
1720
public event PropertyChangedEventHandler? PropertyChanged;
1821

1922
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
@@ -23,7 +26,64 @@ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
2326

2427
public MainWindowViewModel()
2528
{
26-
Keys = new ObservableCollection<ClipViewModel>();
29+
_context = new ClipDbContext();
30+
_context.Database.EnsureCreated();
31+
32+
Console.WriteLine($"Database path: {_context.DbPath}.");
33+
34+
FileMakerClips = new ObservableCollection<ClipViewModel>();
35+
36+
foreach (var clip in _context.Clips)
37+
{
38+
FileMakerClips.Add(new ClipViewModel(
39+
new FileMakerClip(
40+
clip.ClipName,
41+
clip.ClipType,
42+
clip.ClipXml
43+
),
44+
clip.ClipId
45+
)
46+
);
47+
}
48+
}
49+
50+
public void SaveToDb()
51+
{
52+
var dbClips = _context.Clips.ToList();
53+
54+
foreach (var clip in FileMakerClips)
55+
{
56+
var dbClip = dbClips.FirstOrDefault(dbc => dbc.ClipName == clip.Name);
57+
58+
if (dbClip is not null)
59+
{
60+
dbClip.ClipType = clip.ClipType;
61+
dbClip.ClipXml = clip.ClipXml;
62+
}
63+
else
64+
{
65+
_context.Clips.Add(new Clip()
66+
{
67+
ClipName = clip.Name,
68+
ClipType = clip.ClipType,
69+
ClipXml = clip.ClipXml
70+
});
71+
}
72+
}
73+
74+
_context.SaveChanges();
75+
}
76+
77+
public void ClearDb()
78+
{
79+
var clips = _context.Clips.ToList();
80+
81+
foreach (var clip in clips)
82+
{
83+
_context.Clips.Remove(clip);
84+
}
85+
86+
_context.SaveChanges();
2787
}
2888

2989
public void ExitApplication()
@@ -41,7 +101,7 @@ public void NewEmptyItem()
41101
var clip = new FileMakerClip("New", FileMakerClip.ClipTypes.First()?.KeyId ?? "", Array.Empty<byte>());
42102
var clipVm = new ClipViewModel(clip);
43103

44-
Keys.Add(clipVm);
104+
FileMakerClips.Add(clipVm);
45105
}
46106
catch (Exception e)
47107
{
@@ -73,7 +133,7 @@ public void CopyAsClass()
73133
}
74134
}
75135

76-
public async Task PasteFileMakerClipData(CancellationToken token)
136+
public async Task PasteFileMakerClipData()
77137
{
78138
try
79139
{
@@ -102,20 +162,20 @@ public async Task PasteFileMakerClipData(CancellationToken token)
102162

103163
// don't bother adding a duplicate. For some reason entries were getting entered twice per clip
104164
// this is not the most efficient method to detect it, but it works well enough for now
105-
if (Keys.Any(k => k.ClipXml == clip.XmlData))
165+
if (FileMakerClips.Any(k => k.ClipXml == clip.XmlData))
106166
{
107167
continue;
108168
}
109169

110-
Keys.Add(new ClipViewModel(clip));
170+
FileMakerClips.Add(new ClipViewModel(clip));
111171
}
112172
}
113173
catch (Exception e)
114174
{
115175
}
116176
}
117177

118-
public async Task CopySelectedToClip(CancellationToken token)
178+
public async Task CopySelectedToClip()
119179
{
120180
try
121181
{
@@ -140,6 +200,9 @@ public async Task CopySelectedToClip(CancellationToken token)
140200
}
141201
}
142202

203+
/// <summary>
204+
/// SharpFM Version.
205+
/// </summary>
143206
public string Version
144207
{
145208
get
@@ -150,7 +213,7 @@ public string Version
150213
}
151214
}
152215

153-
public ObservableCollection<ClipViewModel> Keys { get; set; }
216+
public ObservableCollection<ClipViewModel> FileMakerClips { get; set; }
154217

155218
private ClipViewModel? _selectedClip;
156219
public ClipViewModel? SelectedClip

SharpFM.Core/FileMakerClip.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ public class ClipFormat
2727
new ClipFormat() { KeyId = "Mac-XMSC", DisplayName = "Script" }
2828
};
2929

30+
public FileMakerClip(string name, string format, string xml)
31+
{
32+
// grab the input clip name
33+
Name = name;
34+
35+
// load the format
36+
ClipboardFormat = format;
37+
38+
try
39+
{
40+
XmlData = PrettyXml(xml);
41+
}
42+
catch
43+
{
44+
XmlData = xml;
45+
}
46+
}
47+
3048
/// <summary>
3149
/// Constructor taking in the raw data byte array.
3250
/// </summary>

0 commit comments

Comments
 (0)