diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs
index 8d2c6df24..06f8cca19 100644
--- a/Flow.Launcher.Core/Plugin/PluginManager.cs
+++ b/Flow.Launcher.Core/Plugin/PluginManager.cs
@@ -48,9 +48,14 @@ namespace Flow.Launcher.Core.Plugin
{
foreach (var plugin in AllPlugins)
{
- var savable = plugin.Plugin as ISavable;
+ var savable = plugin.Plugin as ISettingsSavable;
savable?.Save();
}
+
+ // Everything plugin
+ var savableEverything = AllPlugins.FirstOrDefault(x => x.Metadata.ID == "D2D2C23B084D411DB66FE0C79D6C2A6E")?.Plugin as ISavable;
+ savableEverything?.Save();
+
API.SavePluginSettings();
}
diff --git a/Flow.Launcher.Infrastructure/Storage/ISavable.cs b/Flow.Launcher.Infrastructure/Storage/ISavable.cs
new file mode 100644
index 000000000..155f1af61
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/Storage/ISavable.cs
@@ -0,0 +1,11 @@
+namespace Flow.Launcher.Infrastructure.Storage
+{
+ ///
+ /// Save plugin settings/cache,
+ /// todo should be merged into a abstract class intead of seperate interface
+ ///
+ public interface ISavable
+ {
+ void Save();
+ }
+}
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
index 43d19bea8..6863b2b57 100644
--- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
+++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
@@ -87,4 +87,94 @@ namespace Flow.Launcher.Infrastructure.Storage
File.WriteAllText(FilePath, serialized);
}
}
+
+ public class JsonStrorage where T : new()
+ {
+ private readonly JsonSerializerOptions _serializerSettings;
+ private T _data;
+ // need a new directory name
+ public const string DirectoryName = "Settings";
+ public const string FileSuffix = ".json";
+ public string FilePath { get; set; }
+ public string DirectoryPath { get; set; }
+
+
+ internal JsonStrorage()
+ {
+ // use property initialization instead of DefaultValueAttribute
+ // easier and flexible for default value of object
+ _serializerSettings = new JsonSerializerOptions
+ {
+ IgnoreNullValues = false
+ };
+ }
+
+ public T Load()
+ {
+ if (File.Exists(FilePath))
+ {
+ var searlized = File.ReadAllText(FilePath);
+ if (!string.IsNullOrWhiteSpace(searlized))
+ {
+ Deserialize(searlized);
+ }
+ else
+ {
+ LoadDefault();
+ }
+ }
+ else
+ {
+ LoadDefault();
+ }
+ return _data.NonNull();
+ }
+
+ private void Deserialize(string searlized)
+ {
+ try
+ {
+ _data = JsonSerializer.Deserialize(searlized, _serializerSettings);
+ }
+ catch (JsonException e)
+ {
+ LoadDefault();
+ Log.Exception($"|JsonStrorage.Deserialize|Deserialize error for json <{FilePath}>", e);
+ }
+
+ if (_data == null)
+ {
+ LoadDefault();
+ }
+ }
+
+ private void LoadDefault()
+ {
+ if (File.Exists(FilePath))
+ {
+ BackupOriginFile();
+ }
+
+ _data = new T();
+ Save();
+ }
+
+ private void BackupOriginFile()
+ {
+ var timestamp = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fffffff", CultureInfo.CurrentUICulture);
+ var directory = Path.GetDirectoryName(FilePath).NonNull();
+ var originName = Path.GetFileNameWithoutExtension(FilePath);
+ var backupName = $"{originName}-{timestamp}{FileSuffix}";
+ var backupPath = Path.Combine(directory, backupName);
+ File.Copy(FilePath, backupPath, true);
+ // todo give user notification for the backup process
+ }
+
+ public void Save()
+ {
+ string serialized = JsonSerializer.Serialize(_data, new JsonSerializerOptions() { WriteIndented = true });
+
+ File.WriteAllText(FilePath, serialized);
+ }
+ }
}
diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
index c26aef95d..585770e0f 100644
--- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
+++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
@@ -3,9 +3,9 @@ using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher.Infrastructure.Storage
{
- public class PluginJsonStorage :JsonStorage where T : new()
+ public class PluginJsonSettingStorage :JsonStorage where T : new()
{
- public PluginJsonStorage()
+ public PluginJsonSettingStorage()
{
// C# related, add python related below
var dataType = typeof(T);
@@ -16,9 +16,25 @@ namespace Flow.Launcher.Infrastructure.Storage
FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
}
- public PluginJsonStorage(T data) : this()
+ public PluginJsonSettingStorage(T data) : this()
{
_data = data;
}
}
+
+ public class PluginJsonStorage : JsonStrorage where T : new()
+ {
+ public PluginJsonStorage()
+ {
+ // C# releated, add python releated below
+ var dataType = typeof(T);
+ var assemblyName = typeof(T).Assembly.GetName().Name;
+ DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, assemblyName);
+ Helper.ValidateDirectory(DirectoryPath);
+
+ FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
+ }
+ }
+
}
+
diff --git a/Flow.Launcher.Plugin/Interfaces/ISavable.cs b/Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs
similarity index 91%
rename from Flow.Launcher.Plugin/Interfaces/ISavable.cs
rename to Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs
index 6f408dc2e..120a18adc 100644
--- a/Flow.Launcher.Plugin/Interfaces/ISavable.cs
+++ b/Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs
@@ -5,7 +5,7 @@
/// Otherwise if LoadSettingJsonStorage or SaveSettingJsonStorage has been callded,
/// plugin settings will be automatically saved (see Flow.Launcher/PublicAPIInstance.SavePluginSettings) by Flow
///
- public interface ISavable
+ public interface ISettingsSavable
{
void Save();
}
diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs
index f3403696e..6502e735a 100644
--- a/Flow.Launcher/PublicAPIInstance.cs
+++ b/Flow.Launcher/PublicAPIInstance.cs
@@ -177,7 +177,7 @@ namespace Flow.Launcher
public void SaveJsonStorage(T settings) where T : new()
{
var type = typeof(T);
- _pluginJsonStorages[type] = new PluginJsonStorage(settings);
+ _pluginJsonStorages[type] = new PluginJsonSettingStorage(settings);
((PluginJsonStorage) _pluginJsonStorages[type]).Save();
}
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index 4ee15b46d..469be42c1 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -23,7 +23,7 @@ using System.Threading.Channels;
namespace Flow.Launcher.ViewModel
{
- public class MainViewModel : BaseModel, ISavable
+ public class MainViewModel : BaseModel, ISettingsSavable
{
#region Private Fields
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
index 5175970fd..59bd4809d 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs
@@ -13,7 +13,7 @@ using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
namespace Flow.Launcher.Plugin.Program
{
- public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable
+ public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISettingsSavable, IAsyncReloadable
{
internal static Win32[] _win32s { get; set; }
internal static UWP.Application[] _uwps { get; set; }