Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions Flow.Launcher.Core/Plugin/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static class PluginManager

private static PluginsSettings Settings;
private static List<PluginMetadata> _metadatas;
private static List<string> _modifiedPlugins = new();
private static readonly List<string> _modifiedPlugins = new();

/// <summary>
/// Directories that will hold Flow Launcher plugin directory
Expand All @@ -61,10 +61,17 @@ private static void DeletePythonBinding()
/// </summary>
public static void Save()
{
foreach (var plugin in AllPlugins)
foreach (var pluginPair in AllPlugins)
{
var savable = plugin.Plugin as ISavable;
savable?.Save();
var savable = pluginPair.Plugin as ISavable;
try
{
savable?.Save();
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to save plugin {pluginPair.Metadata.Name}", e);
}
}

API.SavePluginSettings();
Expand All @@ -81,14 +88,21 @@ public static async ValueTask DisposePluginsAsync()

private static async Task DisposePluginAsync(PluginPair pluginPair)
{
switch (pluginPair.Plugin)
try
{
switch (pluginPair.Plugin)
{
case IDisposable disposable:
disposable.Dispose();
break;
case IAsyncDisposable asyncDisposable:
await asyncDisposable.DisposeAsync();
break;
}
}
catch (Exception e)
{
case IDisposable disposable:
disposable.Dispose();
break;
case IAsyncDisposable asyncDisposable:
await asyncDisposable.DisposeAsync();
break;
API.LogException(ClassName, $"Failed to dispose plugin {pluginPair.Metadata.Name}", e);
}
}

Expand Down Expand Up @@ -292,7 +306,7 @@ public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Quer
{
Title = $"{metadata.Name}: Failed to respond!",
SubTitle = "Select this result for more info",
IcoPath = Flow.Launcher.Infrastructure.Constant.ErrorIcon,
IcoPath = Constant.ErrorIcon,
PluginDirectory = metadata.PluginDirectory,
ActionKeywordAssigned = query.ActionKeyword,
PluginID = metadata.ID,
Expand Down Expand Up @@ -369,8 +383,8 @@ public static bool ActionKeywordRegistered(string actionKeyword)
{
// this method is only checking for action keywords (defined as not '*') registration
// hence the actionKeyword != Query.GlobalPluginWildcardSign logic
return actionKeyword != Query.GlobalPluginWildcardSign
&& NonGlobalPlugins.ContainsKey(actionKeyword);
return actionKeyword != Query.GlobalPluginWildcardSign
&& NonGlobalPlugins.ContainsKey(actionKeyword);
}

/// <summary>
Expand Down
17 changes: 8 additions & 9 deletions Flow.Launcher.Core/Resource/Internationalization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public class Internationalization
private const string DefaultFile = "en.xaml";
private const string Extension = ".xaml";
private readonly Settings _settings;
private readonly List<string> _languageDirectories = new List<string>();
private readonly List<ResourceDictionary> _oldResources = new List<ResourceDictionary>();
private readonly List<string> _languageDirectories = new();
private readonly List<ResourceDictionary> _oldResources = new();
private readonly string SystemLanguageCode;

public Internationalization(Settings settings)
Expand Down Expand Up @@ -144,7 +144,7 @@ public void ChangeLanguage(string languageCode)
_settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
}

private Language GetLanguageByLanguageCode(string languageCode)
private static Language GetLanguageByLanguageCode(string languageCode)
{
var lowercase = languageCode.ToLower();
var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase);
Expand Down Expand Up @@ -239,7 +239,7 @@ public List<Language> LoadAvailableLanguages()
return list;
}

public string GetTranslation(string key)
public static string GetTranslation(string key)
{
var translation = Application.Current.TryFindResource(key);
if (translation is string)
Expand All @@ -257,8 +257,7 @@ private void UpdatePluginMetadataTranslations()
{
foreach (var p in PluginManager.GetPluginsForInterface<IPluginI18n>())
{
var pluginI18N = p.Plugin as IPluginI18n;
if (pluginI18N == null) return;
if (p.Plugin is not IPluginI18n pluginI18N) return;
try
{
p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle();
Expand All @@ -272,19 +271,19 @@ private void UpdatePluginMetadataTranslations()
}
}

public string LanguageFile(string folder, string language)
private static string LanguageFile(string folder, string language)
{
if (Directory.Exists(folder))
{
string path = Path.Combine(folder, language);
var path = Path.Combine(folder, language);
if (File.Exists(path))
{
return path;
}
else
{
Log.Error($"|Internationalization.LanguageFile|Language path can't be found <{path}>");
string english = Path.Combine(folder, DefaultFile);
var english = Path.Combine(folder, DefaultFile);
if (File.Exists(english))
{
return english;
Expand Down
6 changes: 3 additions & 3 deletions Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public BinaryStorage(string filename)
[Obsolete("This constructor is obsolete. Use BinaryStorage(string filename) instead.")]
public BinaryStorage(string filename, string directoryPath = null!)
{
directoryPath ??= DataLocation.CacheDirectory;
FilesFolders.ValidateDirectory(directoryPath);
DirectoryPath = directoryPath ?? DataLocation.CacheDirectory;
FilesFolders.ValidateDirectory(DirectoryPath);

FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
}

public async ValueTask<T> TryLoadAsync(T defaultData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ namespace Flow.Launcher.Infrastructure.Storage

public FlowLauncherJsonStorage()
{
var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
FilesFolders.ValidateDirectory(directoryPath);
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
FilesFolders.ValidateDirectory(DirectoryPath);

var filename = typeof(T).Name;
FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
}

public new void Save()
Expand Down
8 changes: 4 additions & 4 deletions Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,12 @@ public static string GetPreviousExistingDirectory(Func<string, bool> locationExi
var index = path.LastIndexOf('\\');
if (index > 0 && index < (path.Length - 1))
{
string previousDirectoryPath = path.Substring(0, index + 1);
return locationExists(previousDirectoryPath) ? previousDirectoryPath : "";
string previousDirectoryPath = path[..(index + 1)];
return locationExists(previousDirectoryPath) ? previousDirectoryPath : string.Empty;
}
else
{
return "";
return string.Empty;
}
}

Expand All @@ -285,7 +285,7 @@ public static string ReturnPreviousDirectoryIfIncompleteString(string path)
// not full path, get previous level directory string
var indexOfSeparator = path.LastIndexOf('\\');

return path.Substring(0, indexOfSeparator + 1);
return path[..(indexOfSeparator + 1)];
}

return path;
Expand Down
10 changes: 10 additions & 0 deletions Flow.Launcher/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () =>

RegisterAppDomainExceptions();
RegisterDispatcherUnhandledException();
RegisterTaskSchedulerUnhandledException();

var imageLoadertask = ImageLoader.InitializeAsync();

Expand Down Expand Up @@ -284,6 +285,15 @@ private static void RegisterAppDomainExceptions()
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
}

/// <summary>
/// let exception throw as normal is better for Debug
/// </summary>
[Conditional("RELEASE")]
private static void RegisterTaskSchedulerUnhandledException()
{
TaskScheduler.UnobservedTaskException += ErrorReporting.TaskSchedulerUnobservedTaskException;
}

#endregion

#region IDisposable
Expand Down
11 changes: 10 additions & 1 deletion Flow.Launcher/Helper/ErrorReporting.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using NLog;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Exception;
using NLog;

namespace Flow.Launcher.Helper;

Expand Down Expand Up @@ -30,6 +32,13 @@ public static void DispatcherUnhandledException(object sender, DispatcherUnhandl
e.Handled = true;
}

public static void TaskSchedulerUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
//handle unobserved task exceptions
Application.Current.Dispatcher.Invoke(() => Report(e.Exception));
//prevent application exit, so the user can copy the prompted error info
}

public static string RuntimeInfo()
{
var info =
Expand Down
6 changes: 3 additions & 3 deletions Flow.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,15 +291,15 @@ private async void OnClosing(object sender, CancelEventArgs e)
{
if (!CanClose)
{
CanClose = true;
_notifyIcon.Visible = false;
App.API.SaveAppAllSettings();
e.Cancel = true;
await ImageLoader.WaitSaveAsync();
await PluginManager.DisposePluginsAsync();
Notification.Uninstall();
// After plugins are all disposed, we can close the main window
CanClose = true;
// Use this instead of Close() to avoid InvalidOperationException when calling Close() in OnClosing event
// After plugins are all disposed, we shutdown application to close app
// We use this instead of Close() to avoid InvalidOperationException when calling Close() in OnClosing event
Application.Current.Shutdown();
}
}
Expand Down
14 changes: 8 additions & 6 deletions Flow.Launcher/PublicAPIInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,23 @@ namespace Flow.Launcher
public class PublicAPIInstance : IPublicAPI, IRemovable
{
private readonly Settings _settings;
private readonly Internationalization _translater;
private readonly MainViewModel _mainVM;

// Must use getter to access Application.Current.Resources.MergedDictionaries so earlier
private Theme _theme;
private Theme Theme => _theme ??= Ioc.Default.GetRequiredService<Theme>();

// Must use getter to avoid circular dependency
private Updater _updater;
private Updater Updater => _updater ??= Ioc.Default.GetRequiredService<Updater>();

private readonly object _saveSettingsLock = new();

#region Constructor

public PublicAPIInstance(Settings settings, Internationalization translater, MainViewModel mainVM)
public PublicAPIInstance(Settings settings, MainViewModel mainVM)
{
_settings = settings;
_translater = translater;
_mainVM = mainVM;
GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
Expand Down Expand Up @@ -100,8 +103,7 @@ public event VisibilityChangedEventHandler VisibilityChanged
remove => _mainVM.VisibilityChanged -= value;
}

// Must use Ioc.Default.GetRequiredService<Updater>() to avoid circular dependency
public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(false);
public void CheckForNewUpdate() => _ = Updater.UpdateAppAsync(false);

public void SaveAppAllSettings()
{
Expand Down Expand Up @@ -178,7 +180,7 @@ public void CopyToClipboard(string stringToCopy, bool directCopy = false, bool s

public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;

public string GetTranslation(string key) => _translater.GetTranslation(key);
public string GetTranslation(string key) => Internationalization.GetTranslation(key);

public List<PluginPair> GetAllPlugins() => PluginManager.AllPlugins.ToList();

Expand Down
Loading