Restore dual mail and calendar app entries

This commit is contained in:
Burak Kaan Köse
2026-04-11 01:28:19 +02:00
parent 4cb08f0a98
commit fdb340549d
19 changed files with 756 additions and 336 deletions
+72 -41
View File
@@ -167,19 +167,31 @@ public partial class App : WinoApplication,
if (!_hasConfiguredAccounts)
return ActivateWelcomeWindowAsync();
return ActivateShellWindowAsync(_preferencesService?.DefaultApplicationMode);
return LaunchEntryOrActivateShellAsync(_preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail);
}
private Task OpenMailFromTrayAsync()
=> _hasConfiguredAccounts
? ActivateShellWindowAsync(WinoApplicationMode.Mail)
? LaunchEntryOrActivateShellAsync(WinoApplicationMode.Mail)
: ActivateWelcomeWindowAsync();
private Task OpenCalendarFromTrayAsync()
=> _hasConfiguredAccounts
? ActivateShellWindowAsync(WinoApplicationMode.Calendar)
? LaunchEntryOrActivateShellAsync(WinoApplicationMode.Calendar)
: ActivateWelcomeWindowAsync();
private async Task LaunchEntryOrActivateShellAsync(WinoApplicationMode mode)
{
if (AppEntryConstants.GetPackagedApplicationId(mode) != null)
{
var appEntryLauncher = Services.GetRequiredService<PackagedAppEntryLauncher>();
if (await appEntryLauncher.LaunchAsync(mode))
return;
}
await ActivateShellWindowAsync(mode);
}
private async Task ActivateWelcomeWindowAsync()
{
var windowManager = Services.GetRequiredService<IWinoWindowManager>();
@@ -218,7 +230,7 @@ public partial class App : WinoApplication,
return;
if (mode.HasValue)
shellWindow.HandleAppActivation(GetModeLaunchArgument(mode.Value));
shellWindow.HandleAppActivation(AppEntryConstants.GetModeLaunchArgument(mode.Value));
CloseWelcomeWindowIfPresent();
await ActivateWindowAsync((WindowEx)shellWindow);
@@ -455,7 +467,15 @@ public partial class App : WinoApplication,
TryMarkInitialNotificationActivationHandled())
{
LogActivation($"Processing notification activation from OnLaunched. Arguments: {toastArgs.Argument}");
await HandleToastActivationAsync(toastArgs);
await HandleToastActivationAsync(toastArgs.Argument, toastArgs.UserInput);
return;
}
if (ToastActivationResolver.TryParse(args.Arguments, out var launchToastArguments) &&
TryMarkInitialNotificationActivationHandled())
{
LogActivation($"Processing toast launch activation from OnLaunched. Arguments: {args.Arguments}");
await HandleToastActivationAsync(launchToastArguments);
return;
}
@@ -512,18 +532,18 @@ public partial class App : WinoApplication,
LogActivation($"Processing initial notification activation from application startup. Arguments: {toastArgs.Argument}");
await EnsureActivationInfrastructureAsync();
await HandleToastActivationAsync(toastArgs);
await HandleToastActivationAsync(toastArgs.Argument, toastArgs.UserInput);
}
private void AppNotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args)
{
// AppNotification callbacks are not guaranteed to run on the UI thread.
// Marshal toast handling to the window dispatcher before touching window APIs.
if (MainWindow?.DispatcherQueue?.TryEnqueue(() => _ = HandleToastActivationAsync(args)) == true)
if (MainWindow?.DispatcherQueue?.TryEnqueue(() => _ = HandleToastActivationAsync(args.Argument, args.UserInput)) == true)
return;
LogActivation($"Processing notification activation from NotificationInvoked. Arguments: {args.Argument}");
_ = HandleToastActivationAsync(args);
_ = HandleToastActivationAsync(args.Argument, args.UserInput);
}
private void TryRegisterAppNotifications()
@@ -546,11 +566,9 @@ public partial class App : WinoApplication,
/// <summary>
/// Handles toast notification activation scenarios.
/// </summary>
private async Task HandleToastActivationAsync(AppNotificationActivatedEventArgs toastArgs)
private async Task HandleToastActivationAsync(NotificationArguments toastArguments, IDictionary<string, string>? userInput = null)
{
LogActivation($"Handling app notification activation. Arguments: {toastArgs.Argument}");
var toastArguments = NotificationArguments.Parse(toastArgs.Argument);
LogActivation("Handling app notification activation.");
if (toastArguments.TryGetValue(Constants.ToastStoreUpdateActionKey, out string storeUpdateAction) &&
storeUpdateAction == Constants.ToastStoreUpdateActionInstall)
@@ -572,7 +590,7 @@ public partial class App : WinoApplication,
if (calendarAction == Constants.ToastCalendarSnoozeAction)
{
await HandleCalendarToastSnoozeAsync(toastArgs, calendarItemId);
await HandleCalendarToastSnoozeAsync(userInput, calendarItemId);
return;
}
}
@@ -600,6 +618,17 @@ public partial class App : WinoApplication,
LogActivation("App notification activation did not match any known handler.");
}
private Task HandleToastActivationAsync(string toastArgument, IDictionary<string, string>? userInput = null)
{
if (!ToastActivationResolver.TryParse(toastArgument, out var toastArguments))
{
LogActivation($"Ignoring non-toast launch argument: {toastArgument}");
return Task.CompletedTask;
}
return HandleToastActivationAsync(toastArguments, userInput);
}
private async Task<IWinoShellWindow?> EnsureShellWindowAsync(WinoApplicationMode mode, bool activateWindow, bool suppressStartupFlows = true)
{
var windowManager = Services.GetRequiredService<IWinoWindowManager>();
@@ -612,7 +641,7 @@ public partial class App : WinoApplication,
CreateWindow(
null,
GetModeLaunchArgument(mode),
AppEntryConstants.GetModeLaunchArgument(mode),
new ShellModeActivationContext
{
SuppressStartupFlows = suppressStartupFlows
@@ -671,9 +700,9 @@ public partial class App : WinoApplication,
navigationService.Navigate(WinoPage.EventDetailsPage, target);
}
private async Task HandleCalendarToastSnoozeAsync(AppNotificationActivatedEventArgs toastArgs, Guid calendarItemId)
private async Task HandleCalendarToastSnoozeAsync(IDictionary<string, string>? userInput, Guid calendarItemId)
{
if (!TryGetSnoozeDurationMinutes(toastArgs, out var snoozeDurationMinutes))
if (!TryGetSnoozeDurationMinutes(userInput, out var snoozeDurationMinutes))
return;
var calendarService = Services.GetRequiredService<ICalendarService>();
@@ -682,15 +711,15 @@ public partial class App : WinoApplication,
await calendarService.SnoozeCalendarItemAsync(calendarItemId, snoozedUntilLocal);
}
private static bool TryGetSnoozeDurationMinutes(AppNotificationActivatedEventArgs toastArgs, out int snoozeDurationMinutes)
private bool TryGetSnoozeDurationMinutes(IDictionary<string, string>? userInput, out int snoozeDurationMinutes)
{
snoozeDurationMinutes = 0;
snoozeDurationMinutes = _preferencesService?.DefaultSnoozeDurationInMinutes ?? 0;
if (toastArgs.UserInput == null ||
!toastArgs.UserInput.TryGetValue(Constants.ToastCalendarSnoozeDurationInputId, out var selectedValue) ||
if (userInput == null ||
!userInput.TryGetValue(Constants.ToastCalendarSnoozeDurationInputId, out var selectedValue) ||
selectedValue == null)
{
return false;
return snoozeDurationMinutes > 0;
}
var selectedText = selectedValue.ToString();
@@ -910,7 +939,7 @@ public partial class App : WinoApplication,
if (TryResolveActivationMode(activationArgs, defaultMode, out var activationMode))
{
shellWindow.HandleAppActivation(GetModeLaunchArgument(activationMode));
shellWindow.HandleAppActivation(AppEntryConstants.GetModeLaunchArgument(activationMode));
return;
}
@@ -1049,7 +1078,7 @@ public partial class App : WinoApplication,
MainWindow?.DispatcherQueue?.TryEnqueue(async () =>
{
// Create and activate ShellWindow — ActiveWindowChanged fires and rebinds the dispatcher.
CreateWindow(null, GetModeLaunchArgument(WinoApplicationMode.Mail));
CreateWindow(null, AppEntryConstants.GetModeLaunchArgument(WinoApplicationMode.Mail));
CloseWelcomeWindowIfPresent();
if (MainWindow != null)
await ActivateWindowAsync(MainWindow);
@@ -1085,7 +1114,7 @@ public partial class App : WinoApplication,
CreateWindow(
null,
GetModeLaunchArgument(WinoApplicationMode.Mail),
AppEntryConstants.GetModeLaunchArgument(WinoApplicationMode.Mail),
new ShellModeActivationContext
{
SuppressStartupFlows = true
@@ -1394,33 +1423,44 @@ public partial class App : WinoApplication,
// Handle toast notification activation
var toastArgs = (AppNotificationActivatedEventArgs)args.Data;
LogActivation($"Processing redirected notification activation. Arguments: {toastArgs.Argument}");
_ = HandleToastActivationAsync(toastArgs);
_ = HandleToastActivationAsync(toastArgs.Argument, toastArgs.UserInput);
}
else
{
var shouldActivateWindow = true;
if (MainWindow is IWinoShellWindow shellWindow)
{
if (args.Kind == ExtendedActivationKind.Launch &&
args.Data is ILaunchActivatedEventArgs launchArgs)
{
var launchArguments = launchArgs.Arguments;
if (Program.TryConsumeRedirectedAlternateModeOverride())
if (ToastActivationResolver.TryParse(launchArgs.Arguments, out var launchToastArguments))
{
launchArguments = AppendLaunchArgument(launchArguments, ToggleDefaultModeLaunchArgument);
shouldActivateWindow = ToastActivationResolver.ShouldBringToForeground(launchToastArguments);
LogActivation($"Processing redirected toast launch activation. Arguments: {launchArgs.Arguments}");
_ = HandleToastActivationAsync(launchToastArguments);
}
else
{
var launchArguments = launchArgs.Arguments;
shellWindow.HandleAppActivation(launchArguments, launchArgs.TileId);
if (Program.TryConsumeRedirectedAlternateModeOverride())
{
launchArguments = AppendLaunchArgument(launchArguments, ToggleDefaultModeLaunchArgument);
}
shellWindow.HandleAppActivation(launchArguments, launchArgs.TileId);
}
}
else if (TryResolveActivationMode(args, _preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail, out var redirectedMode))
{
shellWindow.HandleAppActivation(GetModeLaunchArgument(redirectedMode));
shellWindow.HandleAppActivation(AppEntryConstants.GetModeLaunchArgument(redirectedMode));
}
}
// Redirected launches can target a shell window that is currently hidden in the tray.
// Restore it through the window manager so Show/BringToFront/Activate happen together.
if (MainWindow is WindowEx mainWindow)
if (shouldActivateWindow && MainWindow is WindowEx mainWindow)
{
Services.GetRequiredService<IWinoWindowManager>().ActivateWindow(mainWindow);
}
@@ -1434,15 +1474,6 @@ public partial class App : WinoApplication,
_ = HandleRedirectedActivationAsync();
}
private static string GetModeLaunchArgument(WinoApplicationMode mode)
=> mode switch
{
WinoApplicationMode.Calendar => "--mode=calendar",
WinoApplicationMode.Contacts => "--mode=contacts",
WinoApplicationMode.Settings => "--mode=settings",
_ => "--mode=mail"
};
private static string AppendLaunchArgument(string? launchArguments, string launchArgument)
{
return string.IsNullOrWhiteSpace(launchArguments)