Give the tvOS downloads placeholders a focusable Done button

yattee://downloads still reaches the downloads destination on tvOS,
where it renders a bare ContentUnavailableView with nothing focusable,
leaving the remote stuck.
This commit is contained in:
Arkadiusz Fal
2026-10-16 23:58:58 +02:00
parent b85243852a
commit 9002b253d0

View File

@@ -94,21 +94,13 @@ extension NavigationDestination {
ContinueWatchingView()
case .downloads:
#if os(tvOS)
ContentUnavailableView {
Label(String(localized: "home.downloads.title"), systemImage: "arrow.down.circle")
} description: {
Text(String(localized: "home.downloads.notAvailable"))
}
TVDownloadsUnavailableView(title: String(localized: "home.downloads.title"))
#else
DownloadsView()
#endif
case .downloadsStorage:
#if os(tvOS)
ContentUnavailableView {
Label(String(localized: "settings.downloads.storage.title"), systemImage: "arrow.down.circle")
} description: {
Text(String(localized: "home.downloads.notAvailable"))
}
TVDownloadsUnavailableView(title: String(localized: "settings.downloads.storage.title"))
#else
DownloadsStorageView()
#endif
@@ -157,6 +149,32 @@ extension NavigationDestination {
}
}
#if os(tvOS)
// MARK: - tvOS Downloads Placeholder
/// What a downloads destination shows on tvOS, which has no downloads.
///
/// Reachable on tvOS through `yattee://downloads`. Without a focusable element
/// the focus engine has nowhere to land and the remote is stuck, so it carries
/// a Done that pops it.
private struct TVDownloadsUnavailableView: View {
let title: String
@Environment(\.dismiss) private var dismiss
var body: some View {
ContentUnavailableView {
Label(title, systemImage: "arrow.down.circle")
} description: {
Text(String(localized: "home.downloads.notAvailable"))
} actions: {
Button(String(localized: "common.done")) {
dismiss()
}
}
}
}
#endif
// MARK: - Media Source by ID View
/// Helper view that looks up a MediaSource by ID and shows MediaBrowserView.