Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 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
40 changes: 15 additions & 25 deletions Flow.Launcher.Core/Configuration/Portable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class Portable : IPortable
/// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish
/// </summary>
/// <returns></returns>
private UpdateManager NewUpdateManager()
private static UpdateManager NewUpdateManager()
{
var applicationFolderName = Constant.ApplicationDirectory
.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None)
Expand Down Expand Up @@ -81,20 +81,16 @@ public void EnablePortableMode()

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)
Expand All @@ -110,12 +106,10 @@ public void VerifyUserDataAfterMove(string fromLocation, string toLocation)

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()
Expand All @@ -129,18 +123,14 @@ public void CreateUninstallerEntry()
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);
}

///<summary>
Expand Down
20 changes: 18 additions & 2 deletions Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class JsonRPCPluginSettings
protected ConcurrentDictionary<string, object?> Settings { get; set; } = null!;
public required IPublicAPI API { get; init; }

private static readonly string ClassName = nameof(JsonRPCPluginSettings);

private JsonStorage<ConcurrentDictionary<string, object?>> _storage = null!;

private static readonly Thickness SettingPanelMargin = (Thickness)Application.Current.FindResource("SettingPanelMargin");
Expand Down Expand Up @@ -122,12 +124,26 @@ public void UpdateSettings(IReadOnlyDictionary<string, object> settings)

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()
Expand Down
17 changes: 9 additions & 8 deletions Flow.Launcher.Core/Updater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
using System.Net.Http;
using System.Net.Sockets;
using System.Linq;
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;
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
{
Expand Down Expand Up @@ -70,7 +71,7 @@ public async Task UpdateAppAsync(bool silentUpdate = true)

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"),
Expand Down Expand Up @@ -122,7 +123,7 @@ private class GithubRelease
}

// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs
private async Task<UpdateManager> GitHubUpdateManagerAsync(string repository)
private static async Task<UpdateManager> GitHubUpdateManagerAsync(string repository)
{
var uri = new Uri(repository);
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
Expand All @@ -144,9 +145,9 @@ private async Task<UpdateManager> GitHubUpdateManagerAsync(string repository)
return manager;
}

public string NewVersionTips(string version)
private static string NewVersionTips(string version)
{
var translator = InternationalizationManager.Instance;
var translator = Ioc.Default.GetRequiredService<Internationalization>();
var tips = string.Format(translator.GetTranslation("newVersionTips"), version);

return tips;
Expand Down
12 changes: 11 additions & 1 deletion Flow.Launcher.Infrastructure/Image/ImageLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async
});
}

public static async Task Save()
public static async Task SaveAsync()
{
await storageLock.WaitAsync();

Expand All @@ -71,12 +71,22 @@ await _storage.SaveAsync(ImageCache.EnumerateEntries()
.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<List<(string, bool)>> LoadStorageToConcurrentDictionaryAsync()
{
await storageLock.WaitAsync();
Expand Down
35 changes: 34 additions & 1 deletion Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
using System.IO;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;

namespace Flow.Launcher.Infrastructure.Storage
{
public class FlowLauncherJsonStorage<T> : JsonStorage<T> 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<IPublicAPI>();

public FlowLauncherJsonStorage()
{
var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
Expand All @@ -13,5 +22,29 @@ public FlowLauncherJsonStorage()
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);
}
}
}
}
}
33 changes: 33 additions & 0 deletions Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System.IO;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;

namespace Flow.Launcher.Infrastructure.Storage
{
Expand All @@ -8,6 +11,12 @@ 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<IPublicAPI>();

public PluginJsonStorage()
{
// C# related, add python related below
Expand All @@ -23,5 +32,29 @@ 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);
}
}
}
}
11 changes: 11 additions & 0 deletions Flow.Launcher.Infrastructure/Win32Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -488,5 +488,16 @@ or PInvoke.LOCALE_TRANSIENT_KEYBOARD3
}

#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
}
}
8 changes: 8 additions & 0 deletions Flow.Launcher/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,14 @@ protected virtual void Dispose(bool disposing)
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;
}

Expand Down
13 changes: 9 additions & 4 deletions Flow.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -45,8 +52,6 @@ public partial class MainWindow : IDisposable
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;

Expand Down Expand Up @@ -279,15 +284,15 @@ private async void OnLoaded(object sender, RoutedEventArgs _)

private async void OnClosing(object sender, CancelEventArgs e)
{
if (!_canClose)
if (!CanClose)
{
_notifyIcon.Visible = false;
App.API.SaveAppAllSettings();
e.Cancel = true;
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();
}
Expand Down
5 changes: 2 additions & 3 deletions Flow.Launcher/Notification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "<Pending>")]
internal static bool legacy = !Win32Helper.IsNotificationSupported();

internal static void Uninstall()
{
if (!legacy)
Expand All @@ -25,7 +25,6 @@ public static void Show(string title, string subTitle, string iconPath = null)
});
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
private static void ShowInternal(string title, string subTitle, string iconPath = null)
{
// Handle notification for win7/8/early win10
Expand Down
Loading
Loading