Commit 03189ea0 authored by Kourser's avatar Kourser
Browse files

Make the mini-player progress bar scrubbable



Replace the non-interactive ProgressView in the bottom bar with a draggable
ScrubBar (tap or drag to seek). Like the full player, it shows the dragged
position while scrubbing and commits the seek on release, so it doesn't fight
live time updates. Taller touch target than the visible 4pt line.

Co-Authored-By: default avatarClaude <claude@anthropic.com>
parent 607fadf4
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -19,6 +19,46 @@ struct ArtworkView: View {
    }
}

/// A thin progress bar you can tap or drag to seek. Decoupled from live time
/// updates while dragging, so the fill doesn't jump under the finger.
struct ScrubBar: View {
    let progress: Double            // 0...1 (used when not dragging)
    let duration: TimeInterval
    let onSeek: (TimeInterval) -> Void

    @State private var isDragging = false
    @State private var dragFraction: Double = 0

    var body: some View {
        GeometryReader { geometry in
            let width = max(geometry.size.width, 1)
            let fraction = max(0, min(1, isDragging ? dragFraction : progress))
            ZStack {
                Color.clear // comfortable touch target
                Capsule().fill(.quaternary).frame(height: 4)
                HStack(spacing: 0) {
                    Capsule().fill(.tint).frame(width: width * fraction, height: 4)
                    Spacer(minLength: 0)
                }
            }
            .contentShape(Rectangle())
            .gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { value in
                        isDragging = true
                        dragFraction = max(0, min(1, value.location.x / width))
                    }
                    .onEnded { value in
                        let target = max(0, min(1, value.location.x / width))
                        if duration > 0 { onSeek(target * duration) }
                        isDragging = false
                    }
            )
        }
        .frame(height: 14)
    }
}

struct PodcastRow: View {
    let podcast: Podcast

+5 −2
Original line number Diff line number Diff line
@@ -11,8 +11,11 @@ struct NowPlayingBar: View {
    var body: some View {
        if let episode = playback.currentEpisode {
            VStack(spacing: 0) {
                ProgressView(value: playback.progress)
                    .progressViewStyle(.linear)
                ScrubBar(progress: playback.progress, duration: playback.duration) { time in
                    playback.seek(to: time)
                }
                .padding(.horizontal)
                .padding(.top, 4)

                HStack(spacing: 14) {
                    HStack(spacing: 14) {