diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs
index 069154364..2b570d2c0 100644
--- a/Flow.Launcher.Core/Configuration/Portable.cs
+++ b/Flow.Launcher.Core/Configuration/Portable.cs
@@ -22,7 +22,7 @@ namespace Flow.Launcher.Core.Configuration
/// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish
///
///
- private UpdateManager NewUpdateManager()
+ private static UpdateManager NewUpdateManager()
{
var applicationFolderName = Constant.ApplicationDirectory
.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None)
@@ -81,20 +81,16 @@ namespace Flow.Launcher.Core.Configuration
public void RemoveShortcuts()
{
- using (var portabilityUpdater = NewUpdateManager())
- {
- portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu);
- portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop);
- portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup);
- }
+ using var portabilityUpdater = NewUpdateManager();
+ portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu);
+ portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop);
+ portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup);
}
public void RemoveUninstallerEntry()
{
- using (var portabilityUpdater = NewUpdateManager())
- {
- portabilityUpdater.RemoveUninstallerRegistryEntry();
- }
+ using var portabilityUpdater = NewUpdateManager();
+ portabilityUpdater.RemoveUninstallerRegistryEntry();
}
public void MoveUserDataFolder(string fromLocation, string toLocation)
@@ -110,12 +106,10 @@ namespace Flow.Launcher.Core.Configuration
public void CreateShortcuts()
{
- using (var portabilityUpdater = NewUpdateManager())
- {
- portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu, false);
- portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop, false);
- portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup, false);
- }
+ using var portabilityUpdater = NewUpdateManager();
+ portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu, false);
+ portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop, false);
+ portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup, false);
}
public void CreateUninstallerEntry()
@@ -129,18 +123,14 @@ namespace Flow.Launcher.Core.Configuration
subKey2.SetValue("DisplayIcon", Path.Combine(Constant.ApplicationDirectory, "app.ico"), RegistryValueKind.String);
}
- using (var portabilityUpdater = NewUpdateManager())
- {
- _ = portabilityUpdater.CreateUninstallerRegistryEntry();
- }
+ using var portabilityUpdater = NewUpdateManager();
+ _ = portabilityUpdater.CreateUninstallerRegistryEntry();
}
- internal void IndicateDeletion(string filePathTodelete)
+ private static void IndicateDeletion(string filePathTodelete)
{
var deleteFilePath = Path.Combine(filePathTodelete, DataLocation.DeletionIndicatorFile);
- using (var _ = File.CreateText(deleteFilePath))
- {
- }
+ using var _ = File.CreateText(deleteFilePath);
}
///
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
index 7e9cc9a48..bbb6cf638 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs
@@ -1,15 +1,14 @@
-using Flow.Launcher.Infrastructure.Logger;
-using Flow.Launcher.Infrastructure.UserSettings;
-using Flow.Launcher.Plugin;
-using Flow.Launcher.Plugin.SharedCommands;
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
-using Flow.Launcher.Core.Resource;
using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Infrastructure.Logger;
+using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin;
+using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
@@ -43,8 +42,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal IEnumerable Setup()
{
+ // If no plugin is using the language, return empty list
if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase)))
+ {
return new List();
+ }
if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath))
{
@@ -56,24 +58,55 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
var noRuntimeMessage = string.Format(
- InternationalizationManager.Instance.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"),
+ API.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"),
Language,
EnvName,
Environment.NewLine
);
if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
{
- var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
- string selectedFile;
+ var msg = string.Format(API.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
- selectedFile = GetFileFromDialog(msg, FileDialogFilter);
+ var selectedFile = GetFileFromDialog(msg, FileDialogFilter);
if (!string.IsNullOrEmpty(selectedFile))
+ {
PluginsSettingsFilePath = selectedFile;
-
+ }
// Nothing selected because user pressed cancel from the file dialog window
- if (string.IsNullOrEmpty(selectedFile))
- InstallEnvironment();
+ else
+ {
+ var forceDownloadMessage = string.Format(
+ API.GetTranslation("runtimeExecutableInvalidChooseDownload"),
+ Language,
+ EnvName,
+ Environment.NewLine
+ );
+
+ // Let users select valid path or choose to download
+ while (string.IsNullOrEmpty(selectedFile))
+ {
+ if (API.ShowMsgBox(forceDownloadMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
+ {
+ // Continue select file
+ selectedFile = GetFileFromDialog(msg, FileDialogFilter);
+ }
+ else
+ {
+ // User selected no, break the loop
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(selectedFile))
+ {
+ PluginsSettingsFilePath = selectedFile;
+ }
+ else
+ {
+ InstallEnvironment();
+ }
+ }
}
else
{
@@ -86,7 +119,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
else
{
- API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
+ API.ShowMsgBox(string.Format(API.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
Log.Error("PluginsLoader",
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
$"{Language}Environment");
@@ -99,13 +132,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
private void EnsureLatestInstalled(string expectedPath, string currentPath, string installedDirPath)
{
- if (expectedPath == currentPath)
- return;
+ if (expectedPath == currentPath) return;
FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s));
InstallEnvironment();
-
}
internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata);
@@ -126,7 +157,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
return pluginPairs;
}
- private string GetFileFromDialog(string title, string filter = "")
+ private static string GetFileFromDialog(string title, string filter = "")
{
var dlg = new OpenFileDialog
{
@@ -140,7 +171,6 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
var result = dlg.ShowDialog();
return result == DialogResult.OK ? dlg.FileName : string.Empty;
-
}
///
@@ -183,31 +213,33 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
else
{
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName))
+ {
settings.PluginSettings.PythonExecutablePath
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath);
+ }
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName))
+ {
settings.PluginSettings.NodeExecutablePath
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath);
+ }
}
}
private static bool IsUsingPortablePath(string filePath, string pluginEnvironmentName)
{
- if (string.IsNullOrEmpty(filePath))
- return false;
+ if (string.IsNullOrEmpty(filePath)) return false;
// DataLocation.PortableDataPath returns the current portable path, this determines if an out
// of date path is also a portable path.
- var portableAppEnvLocation = $"UserData\\{DataLocation.PluginEnvironments}\\{pluginEnvironmentName}";
+ var portableAppEnvLocation = Path.Combine("UserData", DataLocation.PluginEnvironments, pluginEnvironmentName);
return filePath.Contains(portableAppEnvLocation);
}
private static bool IsUsingRoamingPath(string filePath)
{
- if (string.IsNullOrEmpty(filePath))
- return false;
+ if (string.IsNullOrEmpty(filePath)) return false;
return filePath.StartsWith(DataLocation.RoamingDataPath);
}
@@ -217,8 +249,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
var index = filePath.IndexOf(DataLocation.PluginEnvironments);
// get the substring after "Environments" because we can not determine it dynamically
- var ExecutablePathSubstring = filePath.Substring(index + DataLocation.PluginEnvironments.Count());
- return $"{DataLocation.PluginEnvironmentsPath}{ExecutablePathSubstring}";
+ var executablePathSubstring = filePath[(index + DataLocation.PluginEnvironments.Length)..];
+ return $"{DataLocation.PluginEnvironmentsPath}{executablePathSubstring}";
}
}
}
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs
index 607c19062..fab5738de 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs
@@ -1,10 +1,10 @@
-using Droplex;
+using System.Collections.Generic;
+using System.IO;
+using Droplex;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
-using System.Collections.Generic;
-using System.IO;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
@@ -22,7 +22,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal override string FileDialogFilter => "Python|pythonw.exe";
- internal override string PluginsSettingsFilePath { get => PluginSettings.PythonExecutablePath; set => PluginSettings.PythonExecutablePath = value; }
+ internal override string PluginsSettingsFilePath
+ {
+ get => PluginSettings.PythonExecutablePath;
+ set => PluginSettings.PythonExecutablePath = value;
+ }
internal PythonEnvironment(List pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs
index 399f7cc03..8a4f527ba 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs
@@ -1,10 +1,10 @@
using System.Collections.Generic;
-using Droplex;
-using Flow.Launcher.Infrastructure.UserSettings;
-using Flow.Launcher.Plugin.SharedCommands;
-using Flow.Launcher.Plugin;
using System.IO;
+using Droplex;
using Flow.Launcher.Core.Plugin;
+using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin;
+using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
@@ -19,7 +19,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
- internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; }
+ internal override string PluginsSettingsFilePath
+ {
+ get => PluginSettings.NodeExecutablePath;
+ set => PluginSettings.NodeExecutablePath = value;
+ }
internal TypeScriptEnvironment(List pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs
index e8cb72e11..61fd28376 100644
--- a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs
+++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs
@@ -1,10 +1,10 @@
using System.Collections.Generic;
-using Droplex;
-using Flow.Launcher.Infrastructure.UserSettings;
-using Flow.Launcher.Plugin.SharedCommands;
-using Flow.Launcher.Plugin;
using System.IO;
+using Droplex;
using Flow.Launcher.Core.Plugin;
+using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin;
+using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
@@ -19,7 +19,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
- internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; }
+ internal override string PluginsSettingsFilePath
+ {
+ get => PluginSettings.NodeExecutablePath;
+ set => PluginSettings.NodeExecutablePath = value;
+ }
internal TypeScriptV2Environment(List pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs
index 944b2fd10..e0a217251 100644
--- a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs
+++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs
@@ -23,6 +23,8 @@ namespace Flow.Launcher.Core.Plugin
protected ConcurrentDictionary Settings { get; set; } = null!;
public required IPublicAPI API { get; init; }
+ private static readonly string ClassName = nameof(JsonRPCPluginSettings);
+
private JsonStorage> _storage = null!;
private static readonly Thickness SettingPanelMargin = (Thickness)Application.Current.FindResource("SettingPanelMargin");
@@ -122,12 +124,26 @@ namespace Flow.Launcher.Core.Plugin
public async Task SaveAsync()
{
- await _storage.SaveAsync();
+ try
+ {
+ await _storage.SaveAsync();
+ }
+ catch (System.Exception e)
+ {
+ API.LogException(ClassName, $"Failed to save plugin settings to path: {SettingPath}", e);
+ }
}
public void Save()
{
- _storage.Save();
+ try
+ {
+ _storage.Save();
+ }
+ catch (System.Exception e)
+ {
+ API.LogException(ClassName, $"Failed to save plugin settings to path: {SettingPath}", e);
+ }
}
public bool NeedCreateSettingPanel()
diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs
index fa4e43e07..c167a5b21 100644
--- a/Flow.Launcher.Core/Plugin/PluginManager.cs
+++ b/Flow.Launcher.Core/Plugin/PluginManager.cs
@@ -1,20 +1,19 @@
-using Flow.Launcher.Core.ExternalPlugins;
-using System;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.DependencyInjection;
+using Flow.Launcher.Core.ExternalPlugins;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
-using ISavable = Flow.Launcher.Plugin.ISavable;
using Flow.Launcher.Plugin.SharedCommands;
-using System.Text.Json;
-using Flow.Launcher.Core.Resource;
-using CommunityToolkit.Mvvm.DependencyInjection;
+using ISavable = Flow.Launcher.Plugin.ISavable;
namespace Flow.Launcher.Core.Plugin
{
diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs
index 9a77ece32..20b884b3d 100644
--- a/Flow.Launcher.Core/Updater.cs
+++ b/Flow.Launcher.Core/Updater.cs
@@ -4,10 +4,12 @@ using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows;
-using JetBrains.Annotations;
-using Squirrel;
+using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure;
@@ -15,8 +17,8 @@ using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
-using System.Text.Json.Serialization;
-using System.Threading;
+using JetBrains.Annotations;
+using Squirrel;
namespace Flow.Launcher.Core
{
@@ -51,7 +53,7 @@ namespace Flow.Launcher.Core
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
var currentVersion = Version.Parse(Constant.Version);
- Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>");
+ Log.Info($"|Updater.UpdateApp|Future Release <{Formatted(newUpdateInfo.FutureReleaseEntry)}>");
if (newReleaseVersion <= currentVersion)
{
@@ -70,7 +72,7 @@ namespace Flow.Launcher.Core
if (DataLocation.PortableDataLocationInUse())
{
- var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
+ var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion}\\{DataLocation.PortableFolderName}";
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s));
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)))
_api.ShowMsgBox(string.Format(_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
@@ -122,7 +124,7 @@ namespace Flow.Launcher.Core
}
// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs
- private async Task GitHubUpdateManagerAsync(string repository)
+ private static async Task GitHubUpdateManagerAsync(string repository)
{
var uri = new Uri(repository);
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
@@ -144,12 +146,22 @@ namespace Flow.Launcher.Core
return manager;
}
- public string NewVersionTips(string version)
+ private static string NewVersionTips(string version)
{
- var translator = InternationalizationManager.Instance;
+ var translator = Ioc.Default.GetRequiredService();
var tips = string.Format(translator.GetTranslation("newVersionTips"), version);
return tips;
}
+
+ private static string Formatted(T t)
+ {
+ var formatted = JsonSerializer.Serialize(t, new JsonSerializerOptions
+ {
+ WriteIndented = true
+ });
+
+ return formatted;
+ }
}
}
diff --git a/Flow.Launcher.Infrastructure/Helper.cs b/Flow.Launcher.Infrastructure/Helper.cs
index 864d796c7..b02d84ca7 100644
--- a/Flow.Launcher.Infrastructure/Helper.cs
+++ b/Flow.Launcher.Infrastructure/Helper.cs
@@ -1,19 +1,11 @@
#nullable enable
using System;
-using System.IO;
-using System.Text.Json;
-using System.Text.Json.Serialization;
namespace Flow.Launcher.Infrastructure
{
public static class Helper
{
- static Helper()
- {
- jsonFormattedSerializerOptions.Converters.Add(new JsonStringEnumConverter());
- }
-
///
/// http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy
///
@@ -36,55 +28,5 @@ namespace Flow.Launcher.Infrastructure
throw new NullReferenceException();
}
}
-
- public static void ValidateDataDirectory(string bundledDataDirectory, string dataDirectory)
- {
- if (!Directory.Exists(dataDirectory))
- {
- Directory.CreateDirectory(dataDirectory);
- }
-
- foreach (var bundledDataPath in Directory.GetFiles(bundledDataDirectory))
- {
- var data = Path.GetFileName(bundledDataPath);
- var dataPath = Path.Combine(dataDirectory, data.NonNull());
- if (!File.Exists(dataPath))
- {
- File.Copy(bundledDataPath, dataPath);
- }
- else
- {
- var time1 = new FileInfo(bundledDataPath).LastWriteTimeUtc;
- var time2 = new FileInfo(dataPath).LastWriteTimeUtc;
- if (time1 != time2)
- {
- File.Copy(bundledDataPath, dataPath, true);
- }
- }
- }
- }
-
- public static void ValidateDirectory(string path)
- {
- if (!Directory.Exists(path))
- {
- Directory.CreateDirectory(path);
- }
- }
-
- private static readonly JsonSerializerOptions jsonFormattedSerializerOptions = new JsonSerializerOptions
- {
- WriteIndented = true
- };
-
- public static string Formatted(this T t)
- {
- var formatted = JsonSerializer.Serialize(t, new JsonSerializerOptions
- {
- WriteIndented = true
- });
-
- return formatted;
- }
}
}
diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
index cca4bd2a4..d0d8ba4fd 100644
--- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
+++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
@@ -62,7 +62,7 @@ namespace Flow.Launcher.Infrastructure.Image
});
}
- public static async Task Save()
+ public static async Task SaveAsync()
{
await storageLock.WaitAsync();
@@ -72,12 +72,22 @@ namespace Flow.Launcher.Infrastructure.Image
.Select(x => x.Key)
.ToList());
}
+ catch (System.Exception e)
+ {
+ Log.Exception($"|ImageLoader.SaveAsync|Failed to save image cache to file", e);
+ }
finally
{
storageLock.Release();
}
}
+ public static async Task WaitSaveAsync()
+ {
+ await storageLock.WaitAsync();
+ storageLock.Release();
+ }
+
private static async Task> LoadStorageToConcurrentDictionaryAsync()
{
await storageLock.WaitAsync();
diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs
index 69ac6000b..81e2503ad 100644
--- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs
+++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs
@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin.SharedCommands;
using MemoryPack;
#nullable enable
@@ -33,7 +34,7 @@ namespace Flow.Launcher.Infrastructure.Storage
public BinaryStorage(string filename)
{
DirectoryPath = DataLocation.CacheDirectory;
- Helper.ValidateDirectory(DirectoryPath);
+ FilesFolders.ValidateDirectory(DirectoryPath);
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
}
diff --git a/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs
index 865041fb3..8b4062b6b 100644
--- a/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs
+++ b/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs
@@ -1,17 +1,51 @@
using System.IO;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin;
+using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Infrastructure.Storage
{
public class FlowLauncherJsonStorage : JsonStorage where T : new()
{
+ private static readonly string ClassName = "FlowLauncherJsonStorage";
+
+ // We should not initialize API in static constructor because it will create another API instance
+ private static IPublicAPI api = null;
+ private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService();
+
public FlowLauncherJsonStorage()
{
var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
- Helper.ValidateDirectory(directoryPath);
+ FilesFolders.ValidateDirectory(directoryPath);
var filename = typeof(T).Name;
FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
}
+
+ public new void Save()
+ {
+ try
+ {
+ base.Save();
+ }
+ catch (System.Exception e)
+ {
+ API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
+ }
+ }
+
+ public new async Task SaveAsync()
+ {
+ try
+ {
+ await base.SaveAsync();
+ }
+ catch (System.Exception e)
+ {
+ API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
index 40106acd8..a3488124b 100644
--- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
+++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Flow.Launcher.Infrastructure.Logger;
+using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Infrastructure.Storage
{
@@ -37,7 +38,7 @@ namespace Flow.Launcher.Infrastructure.Storage
FilePath = filePath;
DirectoryPath = Path.GetDirectoryName(filePath) ?? throw new ArgumentException("Invalid file path");
- Helper.ValidateDirectory(DirectoryPath);
+ FilesFolders.ValidateDirectory(DirectoryPath);
}
public async Task LoadAsync()
diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
index 9c6547c66..b63e8c1ef 100644
--- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
+++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
@@ -1,5 +1,9 @@
using System.IO;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin;
+using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Infrastructure.Storage
{
@@ -8,14 +12,49 @@ namespace Flow.Launcher.Infrastructure.Storage
// Use assembly name to check which plugin is using this storage
public readonly string AssemblyName;
+ private static readonly string ClassName = "PluginJsonStorage";
+
+ // We should not initialize API in static constructor because it will create another API instance
+ private static IPublicAPI api = null;
+ private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService();
+
public PluginJsonStorage()
{
var dataType = typeof(T);
AssemblyName = dataType.Assembly.GetName().Name;
DirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, AssemblyName);
- Helper.ValidateDirectory(DirectoryPath);
+ FilesFolders.ValidateDirectory(DirectoryPath);
FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
}
+
+ public PluginJsonStorage(T data) : this()
+ {
+ Data = data;
+ }
+
+ public new void Save()
+ {
+ try
+ {
+ base.Save();
+ }
+ catch (System.Exception e)
+ {
+ API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
+ }
+ }
+
+ public new async Task SaveAsync()
+ {
+ try
+ {
+ await base.SaveAsync();
+ }
+ catch (System.Exception e)
+ {
+ API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
+ }
+ }
}
}
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 7a3a0c36e..c21849403 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -488,5 +488,16 @@ namespace Flow.Launcher.Infrastructure
}
#endregion
+
+ #region Notification
+
+ public static bool IsNotificationSupported()
+ {
+ // Notifications only supported on Windows 10 19041+
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
+ Environment.OSVersion.Version.Build >= 19041;
+ }
+
+ #endregion
}
}
diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
index 5f003e351..1de5841a5 100644
--- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
+++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
@@ -318,5 +318,51 @@ namespace Flow.Launcher.Plugin.SharedCommands
{
return path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
}
+
+ ///
+ /// Validates a directory, creating it if it doesn't exist
+ ///
+ ///
+ public static void ValidateDirectory(string path)
+ {
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+ }
+
+ ///
+ /// Validates a data directory, synchronizing it by ensuring all files from a bundled source directory exist in it.
+ /// If files are missing or outdated, they are copied from the bundled directory to the data directory.
+ ///
+ ///
+ ///
+ public static void ValidateDataDirectory(string bundledDataDirectory, string dataDirectory)
+ {
+ if (!Directory.Exists(dataDirectory))
+ {
+ Directory.CreateDirectory(dataDirectory);
+ }
+
+ foreach (var bundledDataPath in Directory.GetFiles(bundledDataDirectory))
+ {
+ var data = Path.GetFileName(bundledDataPath);
+ if (data == null) continue;
+ var dataPath = Path.Combine(dataDirectory, data);
+ if (!File.Exists(dataPath))
+ {
+ File.Copy(bundledDataPath, dataPath);
+ }
+ else
+ {
+ var time1 = new FileInfo(bundledDataPath).LastWriteTimeUtc;
+ var time2 = new FileInfo(dataPath).LastWriteTimeUtc;
+ if (time1 != time2)
+ {
+ File.Copy(bundledDataPath, dataPath, true);
+ }
+ }
+ }
+ }
}
}
diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs
index 7b1d113fb..f484d4dba 100644
--- a/Flow.Launcher/App.xaml.cs
+++ b/Flow.Launcher/App.xaml.cs
@@ -138,6 +138,11 @@ namespace Flow.Launcher
{
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
{
+ // Because new message box api uses MessageBoxEx window,
+ // if it is created and closed before main window is created, it will cause the application to exit.
+ // So set to OnExplicitShutdown to prevent the application from shutting down before main window is created
+ Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
+
Log.SetLogLevel(_settings.LogLevel);
Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate();
@@ -299,6 +304,14 @@ namespace Flow.Launcher
return;
}
+ // If we call Environment.Exit(0), the application dispose will be called before _mainWindow.Close()
+ // Accessing _mainWindow?.Dispatcher will cause the application stuck
+ // So here we need to check it and just return so that we will not acees _mainWindow?.Dispatcher
+ if (!_mainWindow.CanClose)
+ {
+ return;
+ }
+
_disposed = true;
}
diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index 3c0e5b153..24ab3cf94 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -9,6 +9,11 @@
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
Please select the {0} executable
+
+ Your selected {0} executable is invalid.
+ {2}{2}
+ Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
+
Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).
Fail to Init Plugins
Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help
@@ -320,6 +325,9 @@
Log Folder
Clear Logs
Are you sure you want to delete all logs?
+ Clear Caches
+ Are you sure you want to delete all caches?
+ Failed to clear part of folders and files. Please see log file for more information
Wizard
User Data Location
User settings and installed plugins are saved in the user data folder. This location may vary depending on whether it's in portable mode or not.
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 011d46d6b..c62606743 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -32,6 +32,13 @@ namespace Flow.Launcher
{
public partial class MainWindow : IDisposable
{
+ #region Public Property
+
+ // Window Event: Close Event
+ public bool CanClose { get; set; } = false;
+
+ #endregion
+
#region Private Fields
// Dependency Injection
@@ -45,8 +52,6 @@ namespace Flow.Launcher
private readonly ContextMenu _contextMenu = new();
private readonly MainViewModel _viewModel;
- // Window Event: Close Event
- private bool _canClose = false;
// Window Event: Key Event
private bool _isArrowKeyPressed = false;
@@ -279,7 +284,7 @@ namespace Flow.Launcher
private async void OnClosing(object sender, CancelEventArgs e)
{
- if (!_canClose)
+ if (!CanClose)
{
_notifyIcon.Visible = false;
App.API.SaveAppAllSettings();
@@ -287,7 +292,7 @@ namespace Flow.Launcher
await PluginManager.DisposePluginsAsync();
Notification.Uninstall();
// After plugins are all disposed, we can close the main window
- _canClose = true;
+ CanClose = true;
// Use this instead of Close() to avoid InvalidOperationException when calling Close() in OnClosing event
Application.Current.Shutdown();
}
diff --git a/Flow.Launcher/Notification.cs b/Flow.Launcher/Notification.cs
index cb1cbb729..30b3a0673 100644
--- a/Flow.Launcher/Notification.cs
+++ b/Flow.Launcher/Notification.cs
@@ -9,8 +9,8 @@ namespace Flow.Launcher
{
internal static class Notification
{
- internal static bool legacy = Environment.OSVersion.Version.Build < 19041;
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "")]
+ internal static bool legacy = !Win32Helper.IsNotificationSupported();
+
internal static void Uninstall()
{
if (!legacy)
@@ -25,7 +25,6 @@ namespace Flow.Launcher
});
}
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "")]
private static void ShowInternal(string title, string subTitle, string iconPath = null)
{
// Handle notification for win7/8/early win10
diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs
index 770195550..99d4cf233 100644
--- a/Flow.Launcher/PublicAPIInstance.cs
+++ b/Flow.Launcher/PublicAPIInstance.cs
@@ -37,6 +37,8 @@ namespace Flow.Launcher
private readonly Internationalization _translater;
private readonly MainViewModel _mainVM;
+ private readonly object _saveSettingsLock = new();
+
#region Constructor
public PublicAPIInstance(Settings settings, Internationalization translater, MainViewModel mainVM)
@@ -57,21 +59,28 @@ namespace Flow.Launcher
_mainVM.ChangeQueryText(query, requery);
}
- public void RestartApp()
+#pragma warning disable VSTHRD100 // Avoid async void methods
+
+ public async void RestartApp()
{
_mainVM.Hide();
- // we must manually save
+ // We must manually save
// UpdateManager.RestartApp() will call Environment.Exit(0)
// which will cause ungraceful exit
SaveAppAllSettings();
+ // Wait for all image caches to be saved before restarting
+ await ImageLoader.WaitSaveAsync();
+
// Restart requires Squirrel's Update.exe to be present in the parent folder,
// it is only published from the project's release pipeline. When debugging without it,
// the project may not restart or just terminates. This is expected.
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
+#pragma warning restore VSTHRD100 // Avoid async void methods
+
public void ShowMainWindow() => _mainVM.Show();
public void HideMainWindow() => _mainVM.Hide();
@@ -85,10 +94,13 @@ namespace Flow.Launcher
public void SaveAppAllSettings()
{
- PluginManager.Save();
- _mainVM.Save();
- _settings.Save();
- _ = ImageLoader.Save();
+ lock (_saveSettingsLock)
+ {
+ _settings.Save();
+ PluginManager.Save();
+ _mainVM.Save();
+ }
+ _ = ImageLoader.SaveAsync();
}
public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync();
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs
index 20f905411..6cfb98306 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs
@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core;
-using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
@@ -16,6 +15,8 @@ namespace Flow.Launcher.SettingPages.ViewModels;
public partial class SettingsPaneAboutViewModel : BaseModel
{
+ private static readonly string ClassName = nameof(SettingsPaneAboutViewModel);
+
private readonly Settings _settings;
private readonly Updater _updater;
@@ -24,7 +25,16 @@ public partial class SettingsPaneAboutViewModel : BaseModel
get
{
var size = GetLogFiles().Sum(file => file.Length);
- return $"{InternationalizationManager.Instance.GetTranslation("clearlogfolder")} ({BytesToReadableString(size)})";
+ return $"{App.API.GetTranslation("clearlogfolder")} ({BytesToReadableString(size)})";
+ }
+ }
+
+ public string CacheFolderSize
+ {
+ get
+ {
+ var size = GetCacheFiles().Sum(file => file.Length);
+ return $"{App.API.GetTranslation("clearcachefolder")} ({BytesToReadableString(size)})";
}
}
@@ -42,7 +52,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
};
public string ActivatedTimes => string.Format(
- InternationalizationManager.Instance.GetTranslation("about_activate_times"),
+ App.API.GetTranslation("about_activate_times"),
_settings.ActivateTimes
);
@@ -88,14 +98,35 @@ public partial class SettingsPaneAboutViewModel : BaseModel
private void AskClearLogFolderConfirmation()
{
var confirmResult = App.API.ShowMsgBox(
- InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"),
- InternationalizationManager.Instance.GetTranslation("clearlogfolder"),
+ App.API.GetTranslation("clearlogfolderMessage"),
+ App.API.GetTranslation("clearlogfolder"),
MessageBoxButton.YesNo
);
if (confirmResult == MessageBoxResult.Yes)
{
- ClearLogFolder();
+ if (!ClearLogFolder())
+ {
+ App.API.ShowMsgBox(App.API.GetTranslation("clearfolderfailMessage"));
+ }
+ }
+ }
+
+ [RelayCommand]
+ private void AskClearCacheFolderConfirmation()
+ {
+ var confirmResult = App.API.ShowMsgBox(
+ App.API.GetTranslation("clearcachefolderMessage"),
+ App.API.GetTranslation("clearcachefolder"),
+ MessageBoxButton.YesNo
+ );
+
+ if (confirmResult == MessageBoxResult.Yes)
+ {
+ if (!ClearCacheFolder())
+ {
+ App.API.ShowMsgBox(App.API.GetTranslation("clearfolderfailMessage"));
+ }
}
}
@@ -113,7 +144,6 @@ public partial class SettingsPaneAboutViewModel : BaseModel
App.API.OpenDirectory(parentFolderPath);
}
-
[RelayCommand]
private void OpenLogsFolder()
{
@@ -121,21 +151,47 @@ public partial class SettingsPaneAboutViewModel : BaseModel
}
[RelayCommand]
- private Task UpdateApp() => _updater.UpdateAppAsync(false);
+ private Task UpdateAppAsync() => _updater.UpdateAppAsync(false);
- private void ClearLogFolder()
+ private bool ClearLogFolder()
{
+ var success = true;
var logDirectory = GetLogDir();
var logFiles = GetLogFiles();
- logFiles.ForEach(f => f.Delete());
+ logFiles.ForEach(f =>
+ {
+ try
+ {
+ f.Delete();
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, $"Failed to delete log file: {f.Name}", e);
+ success = false;
+ }
+ });
logDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
+ // Do not clean log files of current version
.Where(dir => !Constant.Version.Equals(dir.Name))
.ToList()
- .ForEach(dir => dir.Delete());
+ .ForEach(dir =>
+ {
+ try
+ {
+ dir.Delete(true);
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, $"Failed to delete log directory: {dir.Name}", e);
+ success = false;
+ }
+ });
OnPropertyChanged(nameof(LogFolderSize));
+
+ return success;
}
private static DirectoryInfo GetLogDir(string version = "")
@@ -148,6 +204,55 @@ public partial class SettingsPaneAboutViewModel : BaseModel
return GetLogDir(version).EnumerateFiles("*", SearchOption.AllDirectories).ToList();
}
+ private bool ClearCacheFolder()
+ {
+ var success = true;
+ var cacheDirectory = GetCacheDir();
+ var cacheFiles = GetCacheFiles();
+
+ cacheFiles.ForEach(f =>
+ {
+ try
+ {
+ f.Delete();
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, $"Failed to delete cache file: {f.Name}", e);
+ success = false;
+ }
+ });
+
+ cacheDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
+ .ToList()
+ .ForEach(dir =>
+ {
+ try
+ {
+ dir.Delete(true);
+ }
+ catch (Exception e)
+ {
+ App.API.LogException(ClassName, $"Failed to delete cache directory: {dir.Name}", e);
+ success = false;
+ }
+ });
+
+ OnPropertyChanged(nameof(CacheFolderSize));
+
+ return success;
+ }
+
+ private static DirectoryInfo GetCacheDir()
+ {
+ return new DirectoryInfo(DataLocation.CacheDirectory);
+ }
+
+ private static List GetCacheFiles()
+ {
+ return GetCacheDir().EnumerateFiles("*", SearchOption.AllDirectories).ToList();
+ }
+
private static string BytesToReadableString(long bytes)
{
const int scale = 1024;
@@ -156,8 +261,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
foreach (string order in orders)
{
- if (bytes > max)
- return $"{decimal.Divide(bytes, max):##.##} {order}";
+ if (bytes > max) return $"{decimal.Divide(bytes, max):##.##} {order}";
max /= scale;
}
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml
index 75c513411..f7deee7cf 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml
+++ b/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml
@@ -90,6 +90,10 @@
Margin="0 12 0 0"
Icon="">
+
-
-
-
-
+
+
-
-
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
theme ??= Ioc.Default.GetRequiredService();
+
#region Theme Selection
// Theme select codes simplified from SettingsPaneThemeViewModel.cs
@@ -19,24 +23,23 @@ namespace Flow.Launcher.Plugin.Sys
private Theme.ThemeData _selectedTheme;
public Theme.ThemeData SelectedTheme
{
- get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.GetCurrentTheme());
+ get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == Theme.GetCurrentTheme());
set
{
_selectedTheme = value;
- _theme.ChangeTheme(value.FileNameWithoutExtension);
+ Theme.ChangeTheme(value.FileNameWithoutExtension);
- _ = _theme.RefreshFrameAsync();
+ _ = Theme.RefreshFrameAsync();
}
}
- private List Themes => _theme.LoadAvailableThemes();
+ private List Themes => Theme.LoadAvailableThemes();
#endregion
public ThemeSelector(PluginInitContext context)
{
_context = context;
- _theme = Ioc.Default.GetRequiredService();
}
public List Query(Query query)
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs
index 7ad9715bb..76aeb3250 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs
@@ -5,7 +5,6 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
-using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Plugin.WebSearch
@@ -180,7 +179,7 @@ namespace Flow.Launcher.Plugin.WebSearch
// Default images directory is in the WebSearch's application folder
DefaultImagesDirectory = Path.Combine(pluginDirectory, Images);
- Helper.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory);
+ FilesFolders.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory);
// Custom images directory is in the WebSearch's data location folder
CustomImagesDirectory = Path.Combine(_context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "CustomIcons");