Skip to content

Commit

Permalink
feat(storage): added a SQLite persistence layer for saving clips betw…
Browse files Browse the repository at this point in the history
…een runs. (#5)
  • Loading branch information
fuzzzerd authored Sep 29, 2023
1 parent 8aed465 commit e22b32c
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 11 deletions.
11 changes: 8 additions & 3 deletions SharpFM.App/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@
<MenuItem Header="_File">
<MenuItem Command="{Binding NewEmptyItem}" Header="_New" />
<Separator />
<MenuItem Command="{Binding SaveToDb}" Header="Save All" />
<Separator />
<MenuItem Command="{Binding ExitApplication}" Header="_Exit" />
</MenuItem>
<MenuItem Header="_Edit">
<MenuItem Command="{Binding CopySelectedToClip}" Header="Copy" />
<MenuItem Command="{Binding PasteFileMakerClipData}" Header="Paste" />
<MenuItem Command="{Binding CopySelectedToClip}" Header="Copy as FileMaker Blob" />
<MenuItem Command="{Binding PasteFileMakerClipData}" Header="Paste From FileMaker Blob" />
</MenuItem>
<MenuItem Header="Transform">
<MenuItem Command="{Binding CopyAsClass}" Header="Copy as C# Class" />
</MenuItem>
<MenuItem Header="Storage">
<MenuItem Command="{Binding ClearDb}" Header="Clear Db" />
</MenuItem>
<MenuItem Header="{Binding Version}" />
</Menu>
<TextBlock />
Expand All @@ -41,7 +46,7 @@

<ListBox
Grid.Column="0"
ItemsSource="{Binding Keys}"
ItemsSource="{Binding FileMakerClips}"
SelectedItem="{Binding SelectedClip}">
<ListBox.ItemTemplate>
<DataTemplate>
Expand Down
27 changes: 27 additions & 0 deletions SharpFM.App/Models/Clip.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace SharpFM.App.Models;

/// <summary>
/// Clip Data Model
/// </summary>
public class Clip
{
/// <summary>
/// Database Id
/// </summary>
public int ClipId { get; set; }

/// <summary>
/// Display name for clip may match Name inside the xml data or may not.
/// </summary>
public string ClipName { get; set; } = string.Empty;

/// <summary>
/// The data format to use when putting the data back on the clipboard for FileMaker.
/// </summary>
public string ClipType { get; set; } = string.Empty;

/// <summary>
/// Raw xml data from the clip.
/// </summary>
public string ClipXml { get; set; } = string.Empty;
}
36 changes: 36 additions & 0 deletions SharpFM.App/Models/ClipDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore;
using System;


namespace SharpFM.App.Models;

/// <summary>
/// Clip Db Context
/// </summary>
public class ClipDbContext : DbContext
{
/// <summary>
/// Clips stored in the Db.
/// </summary>
public DbSet<Clip> Clips => Set<Clip>();

/// <summary>
/// Database path.
/// </summary>
public string DbPath { get; }

/// <summary>
/// Constructor.
/// </summary>
public ClipDbContext()
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = System.IO.Path.Join(path, "sharpFM.db");
}

// The following configures EF to create a Sqlite database file in the
// special "local" folder for your platform.
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
}
1 change: 1 addition & 0 deletions SharpFM.App/SharpFM.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.4" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.1" />
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.11" />
<PackageReference Include="MinVer" Version="4.3.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
16 changes: 15 additions & 1 deletion SharpFM.App/ViewModels/ClipViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,23 @@ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")

public FileMakerClip Clip { get; set; }

public ClipViewModel(FileMakerClip clip)
public ClipViewModel(FileMakerClip clip) : this(clip, null) { }

public ClipViewModel(FileMakerClip clip, int? clipId)
{
Clip = clip;
ClipId = clipId;
}

private int? _clipId;
public int? ClipId
{
get => _clipId;
set
{
_clipId = value;
NotifyPropertyChanged();
}
}

public string ClipType
Expand Down
77 changes: 70 additions & 7 deletions SharpFM.App/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using FluentAvalonia.UI.Data;
using SharpFM.App.Models;
using SharpFM.Core;

namespace SharpFM.App.ViewModels;

public partial class MainWindowViewModel : INotifyPropertyChanged
{
public ClipDbContext _context;

public event PropertyChangedEventHandler? PropertyChanged;

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

public MainWindowViewModel()
{
Keys = new ObservableCollection<ClipViewModel>();
_context = new ClipDbContext();
_context.Database.EnsureCreated();

Console.WriteLine($"Database path: {_context.DbPath}.");

FileMakerClips = new ObservableCollection<ClipViewModel>();

foreach (var clip in _context.Clips)
{
FileMakerClips.Add(new ClipViewModel(
new FileMakerClip(
clip.ClipName,
clip.ClipType,
clip.ClipXml
),
clip.ClipId
)
);
}
}

public void SaveToDb()
{
var dbClips = _context.Clips.ToList();

foreach (var clip in FileMakerClips)
{
var dbClip = dbClips.FirstOrDefault(dbc => dbc.ClipName == clip.Name);

if (dbClip is not null)
{
dbClip.ClipType = clip.ClipType;
dbClip.ClipXml = clip.ClipXml;
}
else
{
_context.Clips.Add(new Clip()
{
ClipName = clip.Name,
ClipType = clip.ClipType,
ClipXml = clip.ClipXml
});
}
}

_context.SaveChanges();
}

public void ClearDb()
{
var clips = _context.Clips.ToList();

foreach (var clip in clips)
{
_context.Clips.Remove(clip);
}

_context.SaveChanges();
}

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

Keys.Add(clipVm);
FileMakerClips.Add(clipVm);
}
catch (Exception e)
{
Expand Down Expand Up @@ -73,7 +133,7 @@ public void CopyAsClass()
}
}

public async Task PasteFileMakerClipData(CancellationToken token)
public async Task PasteFileMakerClipData()
{
try
{
Expand Down Expand Up @@ -102,20 +162,20 @@ public async Task PasteFileMakerClipData(CancellationToken token)

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

Keys.Add(new ClipViewModel(clip));
FileMakerClips.Add(new ClipViewModel(clip));
}
}
catch (Exception e)
{
}
}

public async Task CopySelectedToClip(CancellationToken token)
public async Task CopySelectedToClip()
{
try
{
Expand All @@ -140,6 +200,9 @@ public async Task CopySelectedToClip(CancellationToken token)
}
}

/// <summary>
/// SharpFM Version.
/// </summary>
public string Version
{
get
Expand All @@ -150,7 +213,7 @@ public string Version
}
}

public ObservableCollection<ClipViewModel> Keys { get; set; }
public ObservableCollection<ClipViewModel> FileMakerClips { get; set; }

private ClipViewModel? _selectedClip;
public ClipViewModel? SelectedClip
Expand Down
18 changes: 18 additions & 0 deletions SharpFM.Core/FileMakerClip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,24 @@ public class ClipFormat
new ClipFormat() { KeyId = "Mac-XMSC", DisplayName = "Script" }
};

public FileMakerClip(string name, string format, string xml)
{
// grab the input clip name
Name = name;

// load the format
ClipboardFormat = format;

try
{
XmlData = PrettyXml(xml);
}
catch
{
XmlData = xml;
}
}

/// <summary>
/// Constructor taking in the raw data byte array.
/// </summary>
Expand Down

0 comments on commit e22b32c

Please sign in to comment.