mirror of
https://github.com/yattee/yattee.git
synced 2026-02-19 09:19:46 +00:00
Move close video button from toolbar into now playing card in RemoteControlView
This commit is contained in:
@@ -185,18 +185,6 @@ struct RemoteControlView: View {
|
|||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
deviceHeader
|
deviceHeader
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .primaryAction) {
|
|
||||||
Button(role: .destructive) {
|
|
||||||
Task {
|
|
||||||
await remoteControl?.closeVideo(on: device)
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Label(String(localized: "remoteControl.closeVideo"), systemImage: "xmark")
|
|
||||||
.labelStyle(.iconOnly)
|
|
||||||
}
|
|
||||||
.disabled(remoteState.videoID == nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,95 +346,112 @@ struct RemoteControlView: View {
|
|||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var nowPlayingSection: some View {
|
private var nowPlayingSection: some View {
|
||||||
VStack(spacing: 12) {
|
ZStack(alignment: .topTrailing) {
|
||||||
HStack {
|
VStack(spacing: 12) {
|
||||||
// Thumbnail - only show if there's an active video
|
HStack {
|
||||||
if remoteState.videoID != nil,
|
// Thumbnail - only show if there's an active video
|
||||||
let thumbnailURL = remoteState.thumbnailURL ?? device.currentVideoThumbnailURL {
|
if remoteState.videoID != nil,
|
||||||
AsyncImage(url: thumbnailURL) { phase in
|
let thumbnailURL = remoteState.thumbnailURL ?? device.currentVideoThumbnailURL {
|
||||||
switch phase {
|
AsyncImage(url: thumbnailURL) { phase in
|
||||||
case .success(let image):
|
switch phase {
|
||||||
image
|
case .success(let image):
|
||||||
.resizable()
|
image
|
||||||
.aspectRatio(16/9, contentMode: .fit)
|
.resizable()
|
||||||
.clipShape(.rect(cornerRadius: 8))
|
.aspectRatio(16/9, contentMode: .fit)
|
||||||
case .failure:
|
.clipShape(.rect(cornerRadius: 8))
|
||||||
thumbnailPlaceholder
|
case .failure:
|
||||||
case .empty:
|
thumbnailPlaceholder
|
||||||
thumbnailPlaceholder
|
case .empty:
|
||||||
.overlay(ProgressView())
|
thumbnailPlaceholder
|
||||||
@unknown default:
|
.overlay(ProgressView())
|
||||||
thumbnailPlaceholder
|
@unknown default:
|
||||||
|
thumbnailPlaceholder
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.frame(maxWidth: 120)
|
|
||||||
} else {
|
|
||||||
thumbnailPlaceholder
|
|
||||||
.frame(maxWidth: 120)
|
.frame(maxWidth: 120)
|
||||||
}
|
} else {
|
||||||
|
thumbnailPlaceholder
|
||||||
|
.frame(maxWidth: 120)
|
||||||
|
}
|
||||||
|
|
||||||
// Video info - only show details if there's an active video
|
// Video info - only show details if there's an active video
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
if remoteState.videoID != nil {
|
if remoteState.videoID != nil {
|
||||||
Text(remoteState.videoTitle ?? device.currentVideoTitle ?? String(localized: "remoteControl.noVideo"))
|
Text(remoteState.videoTitle ?? device.currentVideoTitle ?? String(localized: "remoteControl.noVideo"))
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
.lineLimit(2)
|
.lineLimit(2)
|
||||||
|
|
||||||
if let channel = remoteState.channelName {
|
if let channel = remoteState.channelName {
|
||||||
Text(channel)
|
Text(channel)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(String(localized: "remoteControl.noVideo"))
|
||||||
|
.font(.headline)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Text(String(localized: "remoteControl.noVideo"))
|
|
||||||
.font(.headline)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
// Scrubber
|
||||||
}
|
VStack(spacing: 4) {
|
||||||
// Scrubber
|
#if os(tvOS)
|
||||||
VStack(spacing: 4) {
|
ProgressView(value: displayCurrentTime, total: max(remoteState.duration, 1))
|
||||||
#if os(tvOS)
|
.tint(.accentColor)
|
||||||
ProgressView(value: displayCurrentTime, total: max(remoteState.duration, 1))
|
#else
|
||||||
|
Slider(
|
||||||
|
value: Binding(
|
||||||
|
get: { displayCurrentTime },
|
||||||
|
set: { newValue in
|
||||||
|
scrubTime = newValue
|
||||||
|
if !isScrubbing {
|
||||||
|
isScrubbing = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
in: 0...max(remoteState.duration, 1),
|
||||||
|
onEditingChanged: { editing in
|
||||||
|
if !editing {
|
||||||
|
// User released - send seek command
|
||||||
|
estimatedCurrentTime = scrubTime
|
||||||
|
Task {
|
||||||
|
await remoteControl?.seek(to: scrubTime, on: device)
|
||||||
|
}
|
||||||
|
isScrubbing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
.tint(.accentColor)
|
.tint(.accentColor)
|
||||||
#else
|
.disabled(remoteState.duration == 0)
|
||||||
Slider(
|
#endif
|
||||||
value: Binding(
|
|
||||||
get: { displayCurrentTime },
|
|
||||||
set: { newValue in
|
|
||||||
scrubTime = newValue
|
|
||||||
if !isScrubbing {
|
|
||||||
isScrubbing = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
in: 0...max(remoteState.duration, 1),
|
|
||||||
onEditingChanged: { editing in
|
|
||||||
if !editing {
|
|
||||||
// User released - send seek command
|
|
||||||
estimatedCurrentTime = scrubTime
|
|
||||||
Task {
|
|
||||||
await remoteControl?.seek(to: scrubTime, on: device)
|
|
||||||
}
|
|
||||||
isScrubbing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.tint(.accentColor)
|
|
||||||
.disabled(remoteState.duration == 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Text(formatTime(displayCurrentTime))
|
Text(formatTime(displayCurrentTime))
|
||||||
Spacer()
|
Spacer()
|
||||||
Text("-" + formatTime(remoteState.duration - displayCurrentTime))
|
Text("-" + formatTime(remoteState.duration - displayCurrentTime))
|
||||||
|
}
|
||||||
|
.font(.caption.monospacedDigit())
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
.font(.caption.monospacedDigit())
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
|
.padding(.top, 8)
|
||||||
|
|
||||||
|
// Close video button
|
||||||
|
Button(role: .destructive) {
|
||||||
|
Task {
|
||||||
|
await remoteControl?.closeVideo(on: device)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label(String(localized: "remoteControl.closeVideo"), systemImage: "xmark")
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
.disabled(remoteState.videoID == nil)
|
||||||
|
.padding(12)
|
||||||
}
|
}
|
||||||
.padding()
|
|
||||||
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12))
|
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user