Restore the desktop-shortcut prompt after installs - #5223
Conversation
There was a problem hiding this comment.
Pull request overview
Restores desktop-shortcut prompts after package operations by replacing Windows toast notifications with an in-app dialog.
Changes:
- Centralizes pending-shortcut dialog presentation and tray restoration behavior.
- Detects shortcuts earlier after installs and updates.
- Removes the obsolete Windows shortcut toast.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
PackageOperations.cs |
Moves shortcut detection earlier after successful operations. |
MainWindow.axaml.cs |
Prompts for pending shortcuts when restored. |
WindowsAppNotificationBridge.cs |
Removes the obsolete shortcut toast. |
AvaloniaOperationRegistry.cs |
Adds and coordinates the shortcut dialog flow. |
App.axaml.cs |
Routes secondary-instance activation through tray restoration. |
Comments suppressed due to low confidence (2)
src/UniGetUI.Avalonia/Infrastructure/AvaloniaOperationRegistry.cs:343
- Closing this prompt without choosing “Save and close” still reaches
finally, so every displayed shortcut is removed from the pending list even though no keep/delete verdict was saved. Those shortcuts will not be prompted again becauseSaveChanges()is the path that records a decision and already removes handled entries; only clear the open-dialog flag here so a dismissed prompt remains pending.
finally
{
_shortcutDialogOpen = false;
foreach (var shortcut in pending)
DesktopShortcutsDatabase.RemoveFromUnknownShortcuts(shortcut);
}
src/UniGetUI.Avalonia/Infrastructure/AvaloniaOperationRegistry.cs:328
- Returning here drops the only prompt trigger for shortcuts detected while a dialog is open. This can happen when the window is restored with pending shortcut A while another operation is still running: that operation later adds shortcut B, but its post-operation check returns here, and after the A dialog closes the window is already visible so
ShowFromTray()may never retrigger B. Queue a follow-up check or merge newly detected entries after the current dialog completes.
if (_shortcutDialogOpen) return;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
src/UniGetUI.Avalonia/Infrastructure/AvaloniaOperationRegistry.cs:342
- Closing this prompt with Escape or the modal close button bypasses
SaveChanges, but thisfinallyblock still removes every displayed path from the durable pending list. Those shortcuts then have no saved keep/delete verdict and will never be prompted again. Only clear entries after the dialog confirms that changes were saved (while still handling entries explicitly removed from the list).
finally
{
_shortcutDialogOpen = false;
foreach (var shortcut in pending)
DesktopShortcutsDatabase.RemoveFromUnknownShortcuts(shortcut);
src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/DesktopShortcutsDatabase.cs:244
- The pending list now uses
Settingsread/modify/write calls, but this contains-then-add sequence is not atomic. Parallel package operations are supported, so two successful operations can both read the same cached list andSetListcompeting copies; one operation's newly detected shortcut can be lost (and dialog/IPC removals can race similarly). Serialize all pending-list mutations or provide an atomic settings update.
if (!Settings.ListContains(PendingShortcutsKey, shortcut))
{
Logger.Info($"Marking the shortcut {shortcut} to be asked to be deleted");
Settings.AddToList(PendingShortcutsKey, shortcut);
src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/DesktopShortcutsDatabase.cs:195
- The durable pending-shortcut behavior is not exercised by the accompanying test change; that file only swaps its cleanup call. Please add coverage that records an existing shortcut, resets the settings cache to simulate a restart, verifies it reloads, and verifies remove/clear persist, since surviving a process restart is the behavior this change relies on.
public static List<string> GetUnknownShortcuts()
{
return (Settings.GetList<string>(PendingShortcutsKey) ?? [])
.Where(File.Exists)
.ToList();
src/UniGetUI.Avalonia/Infrastructure/AvaloniaOperationRegistry.cs:328
- Returning here drops the prompt request rather than coalescing it. If the window is restored while an operation is still running, the persisted shortcuts can open this dialog; when that operation later adds another shortcut, its post-operation callback reaches this return, and after the first dialog closes nothing rechecks the remaining pending entry while the window stays visible. Queue a follow-up/requery after the active dialog closes.
private static async Task AutoOpenShortcutsDialogAsync(IReadOnlyList<string> shortcuts)
{
if (_shortcutDialogOpen) return;
This pull request refactors how notifications and dialogs for new desktop shortcuts are handled after package operations, shifting from passive toast notifications to an active dialog prompt. It also ensures that the dialog is shown when the main window is restored from the system tray, and centralizes the logic for presenting and tracking the shortcuts dialog. Additionally, some code cleanup and minor logic reordering were performed.
Desktop shortcut notification and dialog handling:
AutoOpenShortcutsDialogAsync) that appears if the user has enabled the relevant setting. The dialog is shown only once at a time and removes handled shortcuts from the database after completion. ([[1]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-0bf3618786f1fbeff27a5f575b083806e65e872c7d8ab21ca34a246707be051dL297-R345),[[2]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-d67ff391c72f9b5a6f3adb6bfe83e7d8e53b7b58d8395bca47ff3d5ba6138496L214-L255))PromptPendingShortcutsIfAnytoAvaloniaOperationRegistry, which checks for pending shortcuts and triggers the dialog if needed. This method is called when the main window is restored from the tray. ([[1]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-0bf3618786f1fbeff27a5f575b083806e65e872c7d8ab21ca34a246707be051dL297-R345),[[2]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-133754dff4c1cb9804c5fabcde40f72eb6a6c36bc32e2e3bcd175e50f5f3a963R1844-R1845))ShowNewShortcutsNotificationmethod fromWindowsAppNotificationBridge, as it is no longer used. ([src/UniGetUI.Avalonia/Infrastructure/WindowsAppNotificationBridge.csL214-L255](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-d67ff391c72f9b5a6f3adb6bfe83e7d8e53b7b58d8395bca47ff3d5ba6138496L214-L255))UI behavior improvements:
MainWindow.ShowFromTray()to callPromptPendingShortcutsIfAny, ensuring users are prompted about new shortcuts when restoring the app from the tray. ([src/UniGetUI.Avalonia/Views/MainWindow.axaml.csR1844-R1845](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-133754dff4c1cb9804c5fabcde40f72eb6a6c36bc32e2e3bcd175e50f5f3a963R1844-R1845))ShowFromTray()instead of directly activating the window, ensuring consistent shortcut dialog prompting. ([src/UniGetUI.Avalonia/App.axaml.csL218-R218](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-62e45b2932bd115a1d10c41ef0ac96322b60aa38f2af2714ffc396b8637ca657L218-R218))Code cleanup and minor logic changes:
PackageOperations.csto avoid duplicate code and ensure correct timing of shortcut processing after install/uninstall operations. ([[1]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-caf66c255b27d4df66df884c213c069783a122c4d3d93400ea0f998aebb3cc7cR606-L616),[[2]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-caf66c255b27d4df66df884c213c069783a122c4d3d93400ea0f998aebb3cc7cR685-R689),[[3]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-caf66c255b27d4df66df884c213c069783a122c4d3d93400ea0f998aebb3cc7cL695-L699))_shortcutDialogOpenflag inAvaloniaOperationRegistryto prevent multiple dialogs from opening simultaneously. ([[1]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-0bf3618786f1fbeff27a5f575b083806e65e872c7d8ab21ca34a246707be051dR39-R40),[[2]](https://github.com/Devolutions/UniGetUI/pull/5223/files#diff-0bf3618786f1fbeff27a5f575b083806e65e872c7d8ab21ca34a246707be051dL297-R345))