From 6898bc3fc00decee7224895ea75908daf0f97f59 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Thu, 16 Jul 2026 00:29:34 -0500 Subject: fix: blank mailing list from Projects, swipe flicker, hidden upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three problems from manual testing. Opening a mailing list from More → Projects showed a blank screen, while the same tap on a project pinned to Home worked. handleTabNavigation reset the target path and appended to it two Task.yields later. When the target tab is already on screen — Projects lives under More — the reset starts an animated pop of the view the user is standing on and the appends land mid-animation. From Home the tab actually changes, so the More stack is quiescent and the appends land cleanly. Each case now builds its path and assigns it once, so SwiftUI gets a single diff with nothing to race. Destructive swipe actions made the row vanish and spring back while the confirmation was still up. role: .destructive makes SwiftUI perform the row removal on activation, which allowsFullSwipe: false does not prevent — the report was a tap, not a full swipe. These buttons only record pending state and wait for an answer, so they are plain buttons tinted red instead. Six sites: the two added here, plus trackers, pastes, and tracker ACLs and labels, which had the same flicker already. The artifacts upload control was invisible. It was declared as a toolbar item from a view that is a segment inside RepositoryDetailView's tab switch rather than its own navigation destination, so it never reached the navigation bar. It is a row in the list now, and also an action on the empty state — the overlay covers the list, and a repository with no artifacts is precisely the one that needs uploading. --- Hutch/App/RootView.swift | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) (limited to 'Hutch/App/RootView.swift') diff --git a/Hutch/App/RootView.swift b/Hutch/App/RootView.swift index 32d1624..0194d63 100644 --- a/Hutch/App/RootView.swift +++ b/Hutch/App/RootView.swift @@ -291,39 +291,45 @@ struct RootView: View { } } + /// Replaces the target tab's path in one assignment. + /// + /// Resetting the path and appending to it afterwards races when the target tab + /// is already the one on screen: the reset starts an animated pop of the view + /// the user is standing on, and the appends land mid-animation, leaving a blank + /// screen. That is why opening a mailing list from a pinned project on Home + /// worked while the same tap under More → Projects did not — one changes tabs + /// and the other does not. + /// + /// Building the whole path first and assigning once gives SwiftUI a single + /// diff, with nothing to race. private func handleTabNavigation(_ target: AppState.TabNavigationTarget) { switch target { case .repository(let repository): - repoPath = NavigationPath() + var path = NavigationPath() + path.append(repository) + repoPath = path appState.selectedTab = .repositories - Task { - await settleNavigationTransition() - repoPath.append(repository) - } case .tracker(let tracker): - ticketsPath = NavigationPath() + var path = NavigationPath() + path.append(tracker) + ticketsPath = path appState.selectedTab = .tickets - Task { - await settleNavigationTransition() - ticketsPath.append(tracker) - } case .mailingList(let mailingList): - morePath = NavigationPath() + // .lists first so back lands on Mailing Lists rather than dead-ending. + var path = NavigationPath() + path.append(MoreRoute.lists) + path.append(MoreRoute.mailingList(mailingList)) + morePath = path appState.selectedTab = .more - Task { - await settleNavigationTransition() - morePath.append(MoreRoute.lists) - morePath.append(MoreRoute.mailingList(mailingList)) - } + case .systemStatus: - morePath = NavigationPath() + var path = NavigationPath() + path.append(MoreRoute.systemStatus) + morePath = path appState.selectedTab = .more - Task { - await settleNavigationTransition() - morePath.append(MoreRoute.systemStatus) - } + case .builds: buildsPath = NavigationPath() appState.selectedTab = .builds -- cgit v1.2.3