|
| 1 | +# PR Review: #33380 - [PR agent] Issue23892.ShellBackButtonShouldWorkOnLongPress - test fix |
| 2 | + |
| 3 | +**Date:** 2026-01-07 | **Issue:** [#33379](https://github.com/dotnet/maui/issues/33379) | **PR:** [#33380](https://github.com/dotnet/maui/pull/33380) |
| 4 | + |
| 5 | +## ✅ Final Recommendation: APPROVE |
| 6 | + |
| 7 | +| Phase | Status | |
| 8 | +|-------|--------| |
| 9 | +| Pre-Flight | ✅ COMPLETE | |
| 10 | +| 🧪 Tests | ✅ COMPLETE | |
| 11 | +| 🚦 Gate | ✅ PASSED | |
| 12 | +| 🔧 Fix | ✅ COMPLETE | |
| 13 | +| 📋 Report | ✅ COMPLETE | |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +<details> |
| 18 | +<summary><strong>📋 Issue Summary</strong></summary> |
| 19 | + |
| 20 | +**Issue #33379**: The UI test `Issue23892.ShellBackButtonShouldWorkOnLongPress` started failing after PR #32456 was merged. |
| 21 | + |
| 22 | +**Test Expectation**: `OnAppearing count: 2` |
| 23 | +**Test Actual**: `OnAppearing count: 1` |
| 24 | + |
| 25 | +**Original Issue #23892**: Using long-press navigation on the iOS back button in Shell does not update `Shell.Current.CurrentPage`. The `Navigated` and `Navigating` events don't fire. |
| 26 | + |
| 27 | +**Platforms Affected:** |
| 28 | +- [x] iOS |
| 29 | +- [ ] Android |
| 30 | +- [ ] Windows |
| 31 | +- [ ] MacCatalyst |
| 32 | + |
| 33 | +</details> |
| 34 | + |
| 35 | +<details> |
| 36 | +<summary><strong>🔍 Deep Regression Analysis - Full Timeline</strong></summary> |
| 37 | + |
| 38 | +## The Regression Chain |
| 39 | + |
| 40 | +This PR addresses a **double regression** - the same functionality was broken twice by subsequent PRs. |
| 41 | + |
| 42 | +### Timeline of Changes to `ShellSectionRenderer.cs` |
| 43 | + |
| 44 | +| Date | PR | Purpose | Key Change | Broke Long-Press? | |
| 45 | +|------|-----|---------|------------|-------------------| |
| 46 | +| Feb 2025 | #24003 | Fix #23892 (long-press back) | Added `_popRequested` flag + `DidPopItem` | ✅ Fixed it | |
| 47 | +| Jul 2025 | #29825 | Fix #29798/#30280 (tab blank issue) | **Removed** `_popRequested`, expanded `DidPopItem` with manual sync | ❌ **Broke it** | |
| 48 | +| Jan 2026 | #32456 | Fix #32425 (navigation hang) | Added null checks, changed `ElementForViewController` | ❌ Maintained broken state | |
| 49 | + |
| 50 | +### PR #24003 - The Original Fix (Feb 2025) |
| 51 | + |
| 52 | +**Problem solved**: Long-press back button didn't trigger navigation events. |
| 53 | + |
| 54 | +**Solution**: Added `_popRequested` flag to distinguish: |
| 55 | +- **User-initiated navigation** (long-press): Call `SendPop()` → triggers `GoToAsync("..")` → fires `OnAppearing` |
| 56 | +- **Programmatic navigation** (code): Skip `SendPop()` to avoid double-navigation |
| 57 | + |
| 58 | +**Key code added**: |
| 59 | +```csharp |
| 60 | +bool _popRequested; |
| 61 | + |
| 62 | +bool DidPopItem(UINavigationBar _, UINavigationItem __) |
| 63 | + => _popRequested || SendPop(); // If not requested, call SendPop |
| 64 | +``` |
| 65 | + |
| 66 | +### PR #29825 - The First Regression (Jul 2025) |
| 67 | + |
| 68 | +**Problem solved**: Tab becomes blank after specific navigation pattern (pop via tab tap, then navigate again, then back). |
| 69 | + |
| 70 | +**What went wrong**: The PR author expanded `DidPopItem` with manual stack synchronization logic (`_shellSection.SyncStackDownTo()`) and **removed the `_popRequested` flag entirely**. |
| 71 | + |
| 72 | +**Result**: `DidPopItem` now ALWAYS does manual sync, never calls `SendPop()` for user-initiated navigation. Long-press navigation stopped triggering `OnAppearing`. |
| 73 | + |
| 74 | +**Why the test didn't catch it**: Unclear - possibly the test wasn't run or was flaky at the time. |
| 75 | + |
| 76 | +### PR #32456 - Maintained the Broken State (Jan 2026) |
| 77 | + |
| 78 | +**Problem solved**: Navigation hangs after rapidly opening/closing pages (iOS 26 specific). |
| 79 | + |
| 80 | +**What it did**: Added null checks to prevent crashes in `DidPopItem` and changed `ElementForViewController` pattern matching. |
| 81 | + |
| 82 | +**Maintained the regression**: The PR kept the broken `DidPopItem` logic from #29825 (no `_popRequested` flag). |
| 83 | + |
| 84 | +**This triggered the test failure**: When #32456 merged to `inflight/candidate`, the existing `Issue23892` test started failing. |
| 85 | + |
| 86 | +</details> |
| 87 | + |
| 88 | +<details> |
| 89 | +<summary><strong>📁 Files Changed</strong></summary> |
| 90 | + |
| 91 | +| File | Type | Changes | |
| 92 | +|------|------|---------| |
| 93 | +| `src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs` | Fix | -20 lines (simplified) | |
| 94 | +| `src/Controls/src/Core/Shell/ShellSection.cs` | Fix | -44 lines (removed `SyncStackDownTo`) | |
| 95 | + |
| 96 | +**Net change:** -49 lines (code reduction) |
| 97 | + |
| 98 | +</details> |
| 99 | + |
| 100 | +<details> |
| 101 | +<summary><strong>💬 PR Discussion Summary</strong></summary> |
| 102 | + |
| 103 | +**Key Comments:** |
| 104 | +- Issue #33379 was filed by @sheiksyedm pointing to the test failure after #32456 merged |
| 105 | +- @kubaflo (author of both #32456 and #33380) created this fix |
| 106 | + |
| 107 | +**Reviewer Feedback:** |
| 108 | +- None yet |
| 109 | + |
| 110 | +**Disagreements to Investigate:** |
| 111 | +| File:Line | Reviewer Says | Author Says | Status | |
| 112 | +|-----------|---------------|-------------|--------| |
| 113 | +| (none) | | | | |
| 114 | + |
| 115 | +**Author Uncertainty:** |
| 116 | +- None expressed |
| 117 | + |
| 118 | +</details> |
| 119 | + |
| 120 | +<details> |
| 121 | +<summary><strong>🧪 Tests</strong></summary> |
| 122 | + |
| 123 | +**Status**: ✅ COMPLETE |
| 124 | + |
| 125 | +- [x] PR includes UI tests (existing test from #24003) |
| 126 | +- [x] Tests reproduce the issue |
| 127 | +- [x] Tests follow naming convention (`IssueXXXXX`) ✅ |
| 128 | + |
| 129 | +**Test Files:** |
| 130 | +- HostApp: `src/Controls/tests/TestCases.HostApp/Issues/Issue23892.cs` |
| 131 | +- NUnit: `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23892.cs` |
| 132 | + |
| 133 | +</details> |
| 134 | + |
| 135 | +<details> |
| 136 | +<summary><strong>🚦 Gate - Test Verification</strong></summary> |
| 137 | + |
| 138 | +**Status**: ✅ PASSED |
| 139 | + |
| 140 | +- [x] Tests PASS with fix |
| 141 | + |
| 142 | +**Test Run:** |
| 143 | +``` |
| 144 | +Platform: iOS |
| 145 | +Test Filter: FullyQualifiedName~Issue23892 |
| 146 | +Result: SUCCESS ✅ |
| 147 | +``` |
| 148 | + |
| 149 | +**Result:** PASSED ✅ - The `Issue23892.ShellBackButtonShouldWorkOnLongPress` test now passes with the PR's fix. |
| 150 | + |
| 151 | +</details> |
| 152 | + |
| 153 | +<details> |
| 154 | +<summary><strong>🔧 Fix Candidates</strong></summary> |
| 155 | + |
| 156 | +**Status**: ✅ COMPLETE |
| 157 | + |
| 158 | +| # | Source | Approach | Test Result | Files Changed | Notes | |
| 159 | +|---|--------|----------|-------------|---------------|-------| |
| 160 | +| 1 | try-fix | Simplified `DidPopItem`: Always call `SendPop()` when stacks are out of sync | ✅ PASS (Issue23892 + Issue29798 + Issue21119) | `ShellSectionRenderer.cs` (-17, +6) | **Simpler AND works!** | |
| 161 | +| PR | PR #33380 (original) | Restore `_popRequested` flag + preserve manual sync from #29825/#32456 | ✅ PASS (Gate) | `ShellSectionRenderer.cs` (+11) | Superseded by update | |
| 162 | +| PR | PR #33380 (updated) | **Adopted try-fix #1** - Stack sync detection, removed `SyncStackDownTo` | ✅ PASS (CI pending) | `ShellSectionRenderer.cs`, `ShellSection.cs` (-49 net) | **CURRENT - matches recommendation** | |
| 163 | + |
| 164 | +**Update (2026-01-08):** Developer @kubaflo adopted the simpler approach recommended in try-fix #1. |
| 165 | + |
| 166 | +**Exhausted:** Yes |
| 167 | +**Selected Fix:** PR #33380 (updated) - Now implements the recommended simpler approach |
| 168 | + |
| 169 | +</details> |
| 170 | + |
| 171 | +--- |
| 172 | + |
| 173 | +## 📋 Final Report |
| 174 | + |
| 175 | +### Recommendation: ✅ APPROVE |
| 176 | + |
| 177 | +**Update (2026-01-08):** Developer @kubaflo has adopted the recommended simpler approach. |
| 178 | + |
| 179 | +### Changes Made by Developer |
| 180 | + |
| 181 | +The PR now implements exactly the simplified stack-sync detection approach: |
| 182 | + |
| 183 | +**ShellSectionRenderer.cs** - Simplified `DidPopItem`: |
| 184 | +```csharp |
| 185 | +bool DidPopItem(UINavigationBar _, UINavigationItem __) |
| 186 | +{ |
| 187 | + if (_shellSection?.Stack is null || NavigationBar?.Items is null) |
| 188 | + return true; |
| 189 | + |
| 190 | + // If stacks are in sync, nothing to do |
| 191 | + if (_shellSection.Stack.Count == NavigationBar.Items.Length) |
| 192 | + return true; |
| 193 | + |
| 194 | + // Stacks out of sync = user-initiated navigation |
| 195 | + return SendPop(); |
| 196 | +} |
| 197 | +``` |
| 198 | + |
| 199 | +**ShellSection.cs** - Removed `SyncStackDownTo` method (44 lines deleted) |
| 200 | + |
| 201 | +### Why This Approach Works |
| 202 | + |
| 203 | +| Scenario | What Happens | |
| 204 | +|----------|--------------| |
| 205 | +| **Tab tap pop** | Shell updates stack BEFORE `DidPopItem` → stacks ARE in sync → returns early (no `SendPop()`) | |
| 206 | +| **Long-press back** | iOS pops directly → Shell stack NOT updated → stacks out of sync → calls `SendPop()` | |
| 207 | + |
| 208 | +### Benefits of Updated PR |
| 209 | + |
| 210 | +| Aspect | Before (Original PR) | After (Updated PR) | |
| 211 | +|--------|---------------------|-------------------| |
| 212 | +| Lines changed | +11 | **-49 net** | |
| 213 | +| New fields | `_popRequested` bool | **None (stateless)** | |
| 214 | +| Complexity | State tracking | **Simple sync check** | |
| 215 | +| `SyncStackDownTo` | Preserved | **Removed** | |
| 216 | + |
| 217 | +### Conclusion |
| 218 | + |
| 219 | +The PR now: |
| 220 | +- ✅ Fixes Issue #33379 (long-press back navigation) |
| 221 | +- ✅ Uses the simpler stateless approach |
| 222 | +- ✅ Removes 49 lines of code |
| 223 | +- ✅ No new state tracking required |
| 224 | +- ⏳ Pending CI verification |
| 225 | + |
| 226 | +**Approve once CI passes.**u |
0 commit comments