tv-anarchy/Sources/TVAnarchy/MiniTransport.swift
Natalie 4a2ceb9781 feat(offline): inline star-to-keep and trash-to-cull on cache rows
Surface the existing pin (keep-from-cull) and per-file delete actions as
visible inline buttons on each offline cache row instead of context-menu-only:
a star toggles protection from auto-cull (and restore-if-missing), a trash
culls that file early. Aligns wording/icons to the star metaphor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:12:41 -04:00

67 lines
3.2 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import TVAnarchyCore
/// Compact transport for a view's header toolbar: previous · 10s · play/pause ·
/// +10s · next, driving the active host. Lets you control playback without
/// leaving the current tab for the Player tab. The play button (when idle) will
/// resume the last played item from its saved position. Other controls are
/// enabled if the host is reachable.
struct MiniTransport: View {
@Bindable var controller: PlayerController
@Environment(\.appTheme) private var appTheme
private var snap: PlayerController.Snapshot { controller.activeSnapshot }
private var status: PlaybackStatus { snap.status }
/// Same "nothing playing" signal the Player tab uses for its title line.
private var idle: Bool { status.title == nil }
var body: some View {
Group {
if appTheme.usesWinampChrome {
HStack(spacing: 4) {
winamp("backward.end.fill", help: "Previous episode or track") { await $0.previous() }
winamp("gobackward.10", help: "Seek back 10 seconds") { await $0.seek(relative: -10) }
let showPlay = idle || status.paused == true
winamp(showPlay ? "play.fill" : "pause.fill", wide: true, help: showPlay ? "Play" : "Pause") { (_: any PlayerTarget) async in
controller.togglePlayPause()
}
winamp("goforward.10", help: "Seek forward 10 seconds") { await $0.seek(relative: 10) }
winamp("forward.end.fill", help: "Next episode or track") { await $0.next(); await $0.resume() }
}
} else {
HStack(spacing: 10) {
button("backward.end.fill", help: "Previous episode or track") { await $0.previous() }
button("gobackward.10", help: "Seek back 10 seconds") { await $0.seek(relative: -10) }
let showPlay = idle || status.paused == true
button(showPlay ? "play.fill" : "pause.fill", big: true, help: showPlay ? "Play" : "Pause") { (_: any PlayerTarget) async in
controller.togglePlayPause()
}
button("goforward.10", help: "Seek forward 10 seconds") { await $0.seek(relative: 10) }
button("forward.end.fill", help: "Next episode or track") { await $0.next(); await $0.resume() }
}
}
}
.disabled(snap.state == .unreachable)
}
private func winamp(_ system: String, wide: Bool = false,
help: String,
_ op: @escaping (any PlayerTarget) async -> Void) -> some View {
WinampTransportButton(
symbol: system,
width: wide ? 34 : 26,
height: 20,
help: help
) { controller.command(op) }
}
private func button(_ system: String, big: Bool = false,
help: String,
_ op: @escaping (any PlayerTarget) async -> Void) -> some View {
Button { controller.command(op) } label: {
Image(systemName: system).font(big ? .title3 : .body)
}
.buttonStyle(.borderless)
.help(help)
}
}