Files
yattee/Yattee/Views/Components/TVSidebarDetailContainer.swift
Arkadiusz Fal 5c7429abf3 Fix tvOS soft-lock in import views when no rows are focusable
When all playlists/subscriptions were imported, every row collapsed to a
non-focusable checkmark and the Add All toolbar item disappeared, leaving
the view with no focusable element. The Menu button then closed the app
instead of popping the navigation stack.

Wrap the import destinations in TVSidebarDetailContainer for visual
consistency and add a Done toolbar item (cancellationAction) that is
always present on tvOS, reachable from any list row via swipe-up.
2026-05-06 22:17:08 +02:00

85 lines
2.5 KiB
Swift

//
// TVSidebarDetailContainer.swift
// Yattee
//
// Decorates a tvOS detail screen with a fixed 400pt left sidebar showing
// a large SF Symbol and a title, matching the look of tvOS settings.
//
#if os(tvOS)
import SwiftUI
struct TVSidebarDetailContainer<Content: View, BottomAction: View>: View {
let content: Content
let bottomAction: BottomAction
var systemImage: String?
var title: String?
init(
systemImage: String? = nil,
title: String? = nil,
@ViewBuilder bottomAction: () -> BottomAction = { EmptyView() },
@ViewBuilder content: () -> Content
) {
self.content = content()
self.bottomAction = bottomAction()
self.systemImage = systemImage
self.title = title
}
var body: some View {
content
.focusSection()
.safeAreaInset(edge: .leading) {
if let systemImage {
VStack(spacing: 0) {
Spacer()
VStack(spacing: 16) {
Image(systemName: systemImage)
.font(.system(size: 80))
.foregroundStyle(.secondary)
if let title {
Text(title)
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
}
}
.allowsHitTesting(false)
if BottomAction.self != EmptyView.self {
bottomAction
.padding(.top, 40)
.focusSection()
}
Spacer()
}
.frame(width: 400)
} else {
Spacer()
.frame(width: 400)
.allowsHitTesting(false)
}
}
}
}
struct TVDismissBottomButton: View {
var title: String = String(localized: "common.done")
var systemImage: String = "chevron.backward"
@Environment(\.dismiss) private var dismiss
var body: some View {
Button {
dismiss()
} label: {
Label(title, systemImage: systemImage)
}
.buttonStyle(TVSettingsButtonStyle())
}
}
#endif