diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index cb60251ed..76c7a4911 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -84,6 +84,15 @@ namespace Flow.Launcher.Plugin /// Optional message subtitle void ShowMsgError(string title, string subTitle = ""); + /// + /// Show the error message using Flow's standard error icon. + /// + /// Message title + /// Message button content + /// Message button action + /// Optional message subtitle + void ShowMsgErrorWithButton(string title, string buttonText, Action buttonAction, string subTitle = ""); + /// /// Show the MainWindow when hiding /// @@ -127,6 +136,27 @@ namespace Flow.Launcher.Plugin /// when true will use main windows as the owner void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true); + /// + /// Show message box with button + /// + /// Message title + /// Message button content + /// Message button action + /// Message subtitle + /// Message icon path (relative path to your plugin folder) + void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle = "", string iconPath = ""); + + /// + /// Show message box with button + /// + /// Message title + /// Message button content + /// Message button action + /// Message subtitle + /// Message icon path (relative path to your plugin folder) + /// when true will use main windows as the owner + void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath, bool useMainWindowAsOwner = true); + /// /// Open setting dialog /// diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index cedced181..59e8cac20 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -171,6 +171,8 @@ namespace Flow.Launcher Current.Resources["SettingWindowFont"] = new FontFamily(_settings.SettingWindowFont); Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(_settings.SettingWindowFont); + Notification.Install(); + Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); API.LogInfo(ClassName, "Begin Flow Launcher startup ----------------------------------------------------"); diff --git a/Flow.Launcher/Notification.cs b/Flow.Launcher/Notification.cs index be81e6ec6..d277c45df 100644 --- a/Flow.Launcher/Notification.cs +++ b/Flow.Launcher/Notification.cs @@ -1,6 +1,7 @@ using Flow.Launcher.Infrastructure; using Microsoft.Toolkit.Uwp.Notifications; using System; +using System.Collections.Generic; using System.IO; using System.Windows; @@ -12,10 +13,31 @@ namespace Flow.Launcher internal static bool legacy = !Win32Helper.IsNotificationSupported(); + private static readonly Dictionary _notificationActions = new(); + + internal static void Install() + { + if (!legacy) + { + ToastNotificationManagerCompat.OnActivated += toastArgs => + { + var actionId = toastArgs.Argument; // Or use toastArgs.UserInput if using input + if (_notificationActions.TryGetValue(actionId, out var action)) + { + action?.Invoke(); + _notificationActions.Remove(actionId); + } + }; + } + } + internal static void Uninstall() { if (!legacy) + { + _notificationActions.Clear(); ToastNotificationManagerCompat.Uninstall(); + } } public static void Show(string title, string subTitle, string iconPath = null) @@ -67,5 +89,58 @@ namespace Flow.Launcher var msg = new Msg(); msg.Show(title, subTitle, iconPath); } + + public static void ShowWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath = null) + { + Application.Current.Dispatcher.Invoke(() => + { + ShowInternalWithButton(title, buttonText, buttonAction, subTitle, iconPath); + }); + } + + private static void ShowInternalWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath = null) + { + // Handle notification for win7/8/early win10 + if (legacy) + { + LegacyShowWithButton(title, buttonText, buttonAction, subTitle, iconPath); + return; + } + + // Using Windows Notification System + var Icon = !File.Exists(iconPath) + ? Path.Combine(Constant.ProgramDirectory, "Images\\app.png") + : iconPath; + + try + { + var guid = Guid.NewGuid().ToString(); + new ToastContentBuilder() + .AddText(title, hintMaxLines: 1) + .AddText(subTitle) + .AddButton(buttonText, ToastActivationType.Background, guid) + .AddAppLogoOverride(new Uri(Icon)) + .Show(); + _notificationActions.Add(guid, buttonAction); + } + catch (InvalidOperationException e) + { + // Temporary fix for the Windows 11 notification issue + // Possibly from 22621.1413 or 22621.1485, judging by post time of #2024 + App.API.LogException(ClassName, "Notification InvalidOperationException Error", e); + LegacyShowWithButton(title, buttonText, buttonAction, subTitle, iconPath); + } + catch (Exception e) + { + App.API.LogException(ClassName, "Notification Error", e); + LegacyShowWithButton(title, buttonText, buttonAction, subTitle, iconPath); + } + } + + private static void LegacyShowWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath) + { + var msg = new MsgWithButton(); + msg.Show(title, buttonText, buttonAction, subTitle, iconPath); + } } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 80d5de53d..a0e680618 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -123,6 +123,9 @@ namespace Flow.Launcher public void ShowMsgError(string title, string subTitle = "") => ShowMsg(title, subTitle, Constant.ErrorIcon, true); + public void ShowMsgErrorWithButton(string title, string buttonText, Action buttonAction, string subTitle = "") => + ShowMsgWithButton(title, buttonText, buttonAction, subTitle, Constant.ErrorIcon, true); + public void ShowMsg(string title, string subTitle = "", string iconPath = "") => ShowMsg(title, subTitle, iconPath, true); @@ -131,6 +134,14 @@ namespace Flow.Launcher Notification.Show(title, subTitle, iconPath); } + public void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle = "", string iconPath = "") => + ShowMsgWithButton(title, buttonText, buttonAction, subTitle, iconPath, true); + + public void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath, bool useMainWindowAsOwner = true) + { + Notification.ShowWithButton(title, buttonText, buttonAction, subTitle, iconPath); + } + public void OpenSettingDialog() { Application.Current.Dispatcher.Invoke(() =>