Present row sheets through the shared sheet host

Each video row carried its own sheets: tappableVideo added the queue
action and resume sheets, the context menu and swipe actions added
add-to-playlist and download options, so a row could declare up to six
sheets, owned by a row that a list may recycle.

Rows now present those through the nearest sheet host, as does the
video info view's resume prompt. The queue action sheet for a tapped
description link becomes a global request instead of being declared
at both the app root and the expanded player.

Sheets take the color scheme where their host sits, so the floating
details panel's dark override no longer reaches them and the
presentationColorScheme workaround is gone.
This commit is contained in:
Arkadiusz Fal
2026-10-04 09:07:05 +02:00
parent 28a75792e8
commit e3d3809f2f
10 changed files with 39 additions and 147 deletions

View File

@@ -7,37 +7,12 @@
import SwiftUI
/// Data for the resume action sheet, used with sheet(item:) to ensure data availability.
struct ResumeSheetData: Identifiable {
let id = UUID()
let video: Video
let resumeTime: TimeInterval
}
/// View modifier that wraps content in a button that plays a video when tapped.
/// When queue is enabled and not empty, shows a sheet with queue options.
/// When queue is empty or disabled, plays the video directly and queues subsequent videos.
// MARK: - Presentation Color Scheme
/// The real system color scheme, published by hosts that override
/// `colorScheme` for their own content (the unpinned floating details panel
/// renders dark over video). Sheets presented from inside such a host use it
/// so they don't get dark text colors on a light system sheet background.
private struct PresentationColorSchemeKey: EnvironmentKey {
static let defaultValue: ColorScheme? = nil
}
extension EnvironmentValues {
var presentationColorScheme: ColorScheme? {
get { self[PresentationColorSchemeKey.self] }
set { self[PresentationColorSchemeKey.self] = newValue }
}
}
struct TappableVideoModifier: ViewModifier {
@Environment(\.appEnvironment) private var appEnvironment
@Environment(\.colorScheme) private var colorScheme
@Environment(\.presentationColorScheme) private var presentationColorScheme
@Environment(\.presentAppSheet) private var presentAppSheet
let video: Video
var startTime: Double? = nil
@@ -56,11 +31,6 @@ struct TappableVideoModifier: ViewModifier {
/// Callback to load more videos via continuation
nonisolated(unsafe) var loadMoreVideos: LoadMoreVideosCallback? = nil
@State private var showingQueueSheet = false
// Resume action sheet state - using item-based sheet to ensure data is available when presented
@State private var resumeSheetData: ResumeSheetData? = nil
// Password alert state (for WebDAV sources)
@State private var showingPasswordAlert = false
@State private var sourceNeedingPassword: MediaSource?
@@ -102,19 +72,6 @@ struct TappableVideoModifier: ViewModifier {
startTime: startTime
)
}
.sheet(isPresented: $showingQueueSheet) {
QueueActionSheet(video: video, queueSource: queueSource)
.environment(\.colorScheme, presentationColorScheme ?? colorScheme)
}
.sheet(item: $resumeSheetData) { data in
ResumeActionSheet(
video: data.video,
resumeTime: data.resumeTime,
onContinue: { playVideoWithStartTime(data.resumeTime) },
onStartOver: { playVideoWithStartTime(0) }
)
.environment(\.colorScheme, presentationColorScheme ?? colorScheme)
}
.alert(String(localized: "common.authenticationRequired"), isPresented: $showingPasswordAlert) {
SecureField(String(localized: "common.password"), text: $passwordInput)
Button(String(localized: "common.cancel"), role: .cancel) {
@@ -165,7 +122,7 @@ struct TappableVideoModifier: ViewModifier {
sourceNeedingPassword = source
showingPasswordAlert = true
} else if shouldShowQueueSheet {
showingQueueSheet = true
presentAppSheet(.queueAction(video, queueSource: queueSource))
} else {
playVideoAndQueueRest()
}
@@ -180,7 +137,7 @@ struct TappableVideoModifier: ViewModifier {
// Now check if we should show queue sheet or play directly
if shouldShowQueueSheet {
showingQueueSheet = true
presentAppSheet(.queueAction(video, queueSource: queueSource))
} else {
playVideoAndQueueRest()
}
@@ -229,7 +186,12 @@ struct TappableVideoModifier: ViewModifier {
playVideoWithStartTime(0)
case .ask:
// Show the resume action sheet with data bundled together
resumeSheetData = ResumeSheetData(video: video, resumeTime: savedProgress)
presentAppSheet(.resumeAction(
video,
resumeTime: savedProgress,
onContinue: { playVideoWithStartTime(savedProgress) },
onStartOver: { playVideoWithStartTime(0) }
))
}
} else {
// No saved progress or video was completed - play from beginning