Files
yattee/Yattee/Core/NavigationCommands.swift
Arkadiusz Fal b26b7d342f Keep navigation state in a router shared by both iOS layouts
The tab bar and sidebar layouts each kept their own navigation paths and
selection, so swapping between them (resizing an iPad Stage Manager
window across the size-class breakpoint) lost every pushed screen and
re-applied the startup tab.

NavigationRouter now holds the selected section, a typed stack per
section keyed by SidebarItem.id, the search text and the sidebar
manager. Both layouts select with SidebarItem and render the router's
state; navigate(to:) pushes onto it directly instead of going through
an observed pendingNavigation, so a request made before a layout
appears is not lost, and a destination pushed before startup keeps its
section. When a layout can't show the selection (a sidebar channel in
the tab bar, a section hidden by customization) it lands on the section
that can host it with that screen pushed first.

This also fixes keyboard section commands in the tab bar layout and the
coordinator's stale tab on iPad. AppTab, pendingNavigation,
selectedSidebarItem and the unused SheetDestination/path API are gone,
and the iOS 18 and 26.1 root views are merged into AdaptiveTabView.
2026-10-03 23:24:50 +02:00

143 lines
4.0 KiB
Swift

//
// NavigationCommands.swift
// Yattee
//
// Menu bar commands for tab navigation.
//
import SwiftUI
#if !os(tvOS)
/// Navigation-related menu bar commands.
/// Works on both macOS and iPadOS 26+.
struct NavigationCommands: Commands {
let appEnvironment: AppEnvironment
private var navigationCoordinator: NavigationCoordinator {
appEnvironment.navigationCoordinator
}
private var settingsManager: SettingsManager {
appEnvironment.settingsManager
}
private var visibleItems: [SidebarMainItem] {
settingsManager.visibleSidebarMainItems()
}
var body: some Commands {
CommandMenu(String(localized: "menu.navigation")) {
// Home is always visible (required)
homeButton
if visibleItems.contains(.subscriptions) {
subscriptionsButton
}
Divider()
if visibleItems.contains(.bookmarks) {
bookmarksButton
}
if visibleItems.contains(.history) {
historyButton
}
if visibleItems.contains(.downloads) {
downloadsButton
}
Divider()
if visibleItems.contains(.channels) {
channelsButton
}
if visibleItems.contains(.sources) {
sourcesButton
}
Divider()
// Search is always visible (required)
searchButton
if visibleItems.contains(.settings) {
settingsButton
}
}
}
private var homeButton: some View {
Button {
navigationCoordinator.router.select(.home)
} label: {
Text(String(localized: "menu.navigation.home"))
}
.keyboardShortcut("1", modifiers: [.command])
}
private var subscriptionsButton: some View {
Button {
navigationCoordinator.router.select(.subscriptionsFeed)
} label: {
Text(String(localized: "menu.navigation.subscriptions"))
}
.keyboardShortcut("2", modifiers: [.command])
}
private var searchButton: some View {
Button {
navigationCoordinator.router.select(.search)
} label: {
Text(String(localized: "menu.navigation.search"))
}
.keyboardShortcut("f", modifiers: [.command])
}
private var bookmarksButton: some View {
Button {
navigationCoordinator.router.select(.bookmarks)
} label: {
Text(String(localized: "menu.navigation.bookmarks"))
}
.keyboardShortcut("3", modifiers: [.command])
}
private var historyButton: some View {
Button {
navigationCoordinator.router.select(.history)
} label: {
Text(String(localized: "menu.navigation.history"))
}
.keyboardShortcut("4", modifiers: [.command])
}
private var downloadsButton: some View {
Button {
navigationCoordinator.router.select(.downloads)
} label: {
Text(String(localized: "menu.navigation.downloads"))
}
.keyboardShortcut("5", modifiers: [.command])
}
private var channelsButton: some View {
Button {
navigationCoordinator.router.select(.manageChannels)
} label: {
Text(String(localized: "menu.navigation.channels"))
}
.keyboardShortcut("6", modifiers: [.command])
}
private var sourcesButton: some View {
Button {
navigationCoordinator.router.select(.sources)
} label: {
Text(String(localized: "menu.navigation.sources"))
}
.keyboardShortcut("7", modifiers: [.command])
}
private var settingsButton: some View {
Button {
navigationCoordinator.router.select(.settings)
} label: {
Text(String(localized: "menu.navigation.settings"))
}
.keyboardShortcut("9", modifiers: [.command])
}
}
#endif