Skip to content

Conversation

@Jaben
Copy link
Member

@Jaben Jaben commented Dec 7, 2025

Summary

  • Adds a new upgrade dialog that displays release notes before downloading updates
  • Shows current version vs. new version with clear visual hierarchy
  • Provides "Upgrade" and "Ignore" buttons so users can review changes first
  • Uses WebView2 to render HTML release notes from Velopack's NotesHTML property

Changes

  • New: UpgradeDialogViewModel.cs - ViewModel with version info and user choice tracking
  • New: UpgradeDialogView.xaml - MahApps.Metro styled dialog with WebView2 for release notes
  • New: UpgradeDialogView.xaml.cs - Code-behind for WebView2 initialization
  • Modified: MainViewModel.UpgradeToLatest() - Shows dialog before proceeding with download

Test plan

  • Click "Upgrade available" notification when an update is available
  • Verify dialog shows current and new version numbers
  • Verify release notes are displayed in the dialog
  • Click "Ignore" and verify no download occurs
  • Click "Upgrade" and verify download proceeds as before

Closes #344

Summary by CodeRabbit

  • New Features
    • Added an upgrade confirmation dialog showing current vs. new version and release notes (renders HTML, with a default message when none provided).
    • Users can choose to Upgrade or Ignore; their choice controls whether the update proceeds.
    • Release notes rendering includes robust initialization and graceful error fallback if content fails to load.

✏️ Tip: You can customize this high-level summary in your review settings.

When clicking the upgrade notification, shows a dialog with:
- Current and new version information
- Release notes from Velopack (NotesHTML)
- Upgrade and Ignore buttons

User can review release notes before deciding to upgrade.

Closes #344
@coderabbitai
Copy link

coderabbitai bot commented Dec 7, 2025

Walkthrough

Adds an interactive upgrade confirmation flow: MainViewModel now shows an UpgradeDialog (view + viewmodel) with current/new versions and release notes (WebView2). The dialog returns an explicit user choice (Upgrade or Ignore) which MainViewModel respects before continuing the download/apply sequence.

Changes

Cohort / File(s) Change Summary
Upgrade flow enhancement
src/Papercut.UI/ViewModels/MainViewModel.cs
Modified UpgradeToLatest to extract release notes and versions, instantiate and show an UpgradeDialogViewModel via the window manager, branch on the returned user choice, and only continue the existing download/apply flow when the user confirms.
New upgrade dialog view model
src/Papercut.UI/ViewModels/UpgradeDialogViewModel.cs
Added UpgradeDialogViewModel with CurrentVersion, NewVersion, ReleaseNotesHtml, UserChoice (enum: None/Upgrade/Ignore), Upgrade() and Ignore() actions, and default fallback HTML when release notes are missing.
New upgrade dialog view
src/Papercut.UI/Views/UpgradeDialogView.xaml, src/Papercut.UI/Views/UpgradeDialogView.xaml.cs
Added MetroWindow XAML showing version header, a WebView2 to render release notes, and Ignore/Upgrade buttons. Code-behind initializes WebView2, navigates to ReleaseNotesHtml, and renders a styled error HTML on exceptions.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant MVM as MainViewModel
    participant WM as WindowManager
    participant UDVM as UpgradeDialogViewModel
    participant UDV as UpgradeDialogView
    participant WebView as WebView2

    User->>MVM: Trigger upgrade flow
    activate MVM
    MVM->>MVM: Retrieve release notes & versions
    MVM->>UDVM: Create with current/new/releaseNotes
    MVM->>WM: Show UDVM (modal)
    activate WM
    WM->>UDV: Instantiate & display dialog
    activate UDV
    UDV->>WebView: Initialize & Navigate(ReleaseNotesHtml)
    WebView-->>UDV: Loaded / Error
    User->>UDV: Click Upgrade or Ignore
    UDV->>UDVM: Invoke Upgrade()/Ignore()
    UDVM-->>MVM: Return user choice
    alt choice == Upgrade
        MVM->>MVM: Log and continue download/apply update
    else choice == Ignore
        MVM->>MVM: Log and return early
    end
    deactivate MVM
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review points:
    • MainViewModel.UpgradetoLatest: correct extraction of release notes and version strings, and proper async/modal handling of the dialog result.
    • UpgradeDialogView.xaml.cs: WebView2 initialization, navigation, and safe error HTML escaping.
    • UpgradeDialogViewModel: ensure TryCloseAsync usage and UserChoice mapping are consistent with window manager expectations.

Possibly related PRs

Poem

🐰 I found new notes in WebView bright,
Versions lined up, a glowing sight,
"Upgrade" or "Ignore" — I tap with cheer,
Hopping forward to the next release year! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'UI: Improve Upgrade Available Experience' directly aligns with the main change: adding an interactive upgrade dialog with release notes display.
Linked Issues check ✅ Passed Changes fully implement #344 requirements: dialog displays release notes, shows current/new versions, and provides Upgrade and Ignore buttons with proper user flow.
Out of Scope Changes check ✅ Passed All changes are scoped to the upgrade dialog feature: new ViewModel, View, View code-behind, and MainViewModel upgrade flow modification—no unrelated alterations detected.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/upgrade-experience-344

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/Papercut.UI/ViewModels/UpgradeDialogViewModel.cs (1)

68-78: Consider using consistent boolean parameter for TryCloseAsync.

Both methods set UserChoice correctly, but Upgrade() passes true to TryCloseAsync while Ignore() passes false. Since the UserChoice property already tracks the user's decision, consider using the same boolean value for consistency unless there's a specific reason for the difference.

If the boolean parameter doesn't affect downstream logic, consider this refactor:

 public void Upgrade()
 {
     this.UserChoice = UpgradeChoice.Upgrade;
     this.TryCloseAsync(true);
 }

 public void Ignore()
 {
     this.UserChoice = UpgradeChoice.Ignore;
-    this.TryCloseAsync(false);
+    this.TryCloseAsync(true);
 }
src/Papercut.UI/ViewModels/MainViewModel.cs (1)

492-514: Consider using injected IViewModelWindowManager for consistency.

The code creates a new WindowManager instance manually (line 503), but this file already uses _viewModelWindowManager for other dialogs (see lines 570, 604, 664). Using the injected window manager ensures consistent dialog behavior and respects any application-wide window manager configuration.

Apply this change to use the existing pattern:

-    // Get the window manager from DI and show the dialog manually
-    var windowManager = new Caliburn.Micro.WindowManager();
-    var dialogResult = await windowManager.ShowDialogAsync(upgradeDialog);
+    // Show the dialog using the injected window manager
+    var dialogResult = await this._viewModelWindowManager.ShowDialogAsync(upgradeDialog);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 893c205 and fc26885.

📒 Files selected for processing (4)
  • src/Papercut.UI/ViewModels/MainViewModel.cs (1 hunks)
  • src/Papercut.UI/ViewModels/UpgradeDialogViewModel.cs (1 hunks)
  • src/Papercut.UI/Views/UpgradeDialogView.xaml (1 hunks)
  • src/Papercut.UI/Views/UpgradeDialogView.xaml.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/Papercut.UI/ViewModels/MainViewModel.cs (2)
src/Papercut.Common/Helper/AssemblyHelper.cs (1)
  • GetVersion (25-30)
src/Papercut.UI/ViewModels/UpgradeDialogViewModel.cs (3)
  • UpgradeDialogViewModel (22-79)
  • UpgradeDialogViewModel (24-58)
  • Upgrade (68-72)
src/Papercut.UI/Views/UpgradeDialogView.xaml.cs (1)
src/Papercut.UI/ViewModels/UpgradeDialogViewModel.cs (2)
  • UpgradeDialogViewModel (22-79)
  • UpgradeDialogViewModel (24-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Papercut SMTP
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Build Papercut SMTP
🔇 Additional comments (4)
src/Papercut.UI/ViewModels/UpgradeDialogViewModel.cs (1)

24-58: LGTM! Clean constructor with appropriate fallback.

The constructor properly handles null or empty release notes with a well-styled fallback HTML message. The fallback provides a good user experience when release notes are unavailable.

src/Papercut.UI/Views/UpgradeDialogView.xaml.cs (2)

31-57: Verify WebView2 runtime availability before showing dialog.

The code initializes WebView2 without checking if the runtime is installed. While error handling is in place (lines 43-55), the user experience would be better if the dialog validates WebView2 availability before being displayed. The MainViewModel has access to _webView2Information.IsInstalled (line 136 in MainViewModel.cs) which could be used to check availability upstream.

Consider adding a check in MainViewModel.UpgradeToLatest() before creating the dialog:

if (!this._webView2Information.IsInstalled)
{
    await this.ShowMessageAsync("Update Information", 
        "Release notes require WebView2. Please proceed with the upgrade to view full release information.");
    // Optionally skip dialog and proceed directly to upgrade
}

Alternatively, verify whether WebView2 is a required dependency for the application and if its absence should prevent showing the dialog entirely.


46-54: Error handling provides good fallback experience.

The error HTML fallback properly uses SecurityElement.Escape() to prevent potential XSS issues when displaying exception messages. This is a good defensive coding practice.

src/Papercut.UI/Views/UpgradeDialogView.xaml (1)

1-88: LGTM! Clean XAML structure with proper MahApps styling.

The view is well-structured with:

  • Clear visual hierarchy (header, content, actions)
  • Consistent use of MahApps.Metro theme resources
  • Appropriate window properties (centered, resizable, not in taskbar)
  • Caliburn.Micro convention-based button naming that correctly binds to view model methods

The design provides a professional upgrade experience consistent with the application's UI patterns.

Replace manual WindowManager instantiation with the injected
_viewModelWindowManager for consistency with other dialogs.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Papercut.UI/ViewModels/MainViewModel.cs (1)

503-503: Consider removing the unused dialogResult variable.

The dialogResult variable is captured but never used. The user's choice is correctly checked via upgradeDialog.UserChoice instead. For cleaner code, consider using a discard:

-        var dialogResult = await this._viewModelWindowManager.ShowDialogAsync(upgradeDialog);
+        _ = await this._viewModelWindowManager.ShowDialogAsync(upgradeDialog);

Alternatively, you could simply await without capturing:

-        var dialogResult = await this._viewModelWindowManager.ShowDialogAsync(upgradeDialog);
+        await this._viewModelWindowManager.ShowDialogAsync(upgradeDialog);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc26885 and 7e50a19.

📒 Files selected for processing (1)
  • src/Papercut.UI/ViewModels/MainViewModel.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/Papercut.UI/ViewModels/MainViewModel.cs (2)
src/Papercut.Common/Helper/AssemblyHelper.cs (1)
  • GetVersion (25-30)
src/Papercut.UI/ViewModels/UpgradeDialogViewModel.cs (3)
  • UpgradeDialogViewModel (22-79)
  • UpgradeDialogViewModel (24-58)
  • Upgrade (68-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Papercut SMTP
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Build Papercut SMTP
🔇 Additional comments (2)
src/Papercut.UI/ViewModels/MainViewModel.cs (2)

506-512: LGTM! User choice handling is correct.

The logic properly handles all cases:

  • Aborts if user chose "Ignore" or closed the dialog without choosing (None)
  • Logs the user's decision appropriately
  • Only proceeds with upgrade when explicitly confirmed

492-500: No action required on Velopack API usage.

The NotesHTML property exists on VelopackAsset and is properly documented in the official Velopack API. The version extraction logic, dialog creation, and release notes retrieval are all correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UI: Improve Upgrade Available Experience

2 participants