Use two-column layout for tvOS video info view

Reworks VideoInfoView on tvOS into a persistent 30% left sidebar
(thumbnail, title, channel, Play / Add to Playlist / Bookmark) with a
scrollable right pane for description, stats, comments, related, and
watch history. Reuses the player's TVScrollableDescription (refactored
to self-manage focus) so the description supports click-to-lock
scrolling, and the outer ScrollView is disabled while locked. Comments
full-screen on tvOS, with commenter avatars no longer tappable and
accent-colored link text replaced with the default foreground.
This commit is contained in:
Arkadiusz Fal
2026-04-16 02:26:51 +02:00
parent 6a45ed7d0f
commit 0aac9168cb
3 changed files with 284 additions and 30 deletions

View File

@@ -134,7 +134,6 @@ struct TVDetailsPanel: View {
if let description = video?.description, !description.isEmpty {
TVScrollableDescription(
description: description,
focusedItem: $focusedItem,
isScrollLocked: $isDescriptionScrollLocked
)
.padding(.top, isDescriptionScrollLocked ? 24 : 8)
@@ -309,17 +308,13 @@ enum TVDetailsFocusItem: Hashable {
/// When locked, expands to fill available space for easier reading.
struct TVScrollableDescription: View {
let description: String
@FocusState.Binding var focusedItem: TVDetailsFocusItem?
@Binding var isScrollLocked: Bool
@FocusState private var isFocused: Bool
@State private var scrollOffset: CGFloat = 0
private let scrollStep: CGFloat = 80
private let maxScroll: CGFloat = 5000
private var isFocused: Bool {
focusedItem == .description
}
var body: some View {
Button {
// Toggle scroll lock on click/select
@@ -333,7 +328,7 @@ struct TVScrollableDescription: View {
descriptionContent
}
.buttonStyle(TVDescriptionButtonStyle(isFocused: isFocused, isLocked: isScrollLocked))
.focused($focusedItem, equals: .description)
.focused($isFocused)
.onMoveCommand { direction in
guard isScrollLocked else { return }