Commit 2e4f0bfe authored by Kourser's avatar Kourser
Browse files

fix(macos): AppKit Touch Bar with working transport and a podcast scrubber

Replaces the SwiftUI .touchBar attempt (whose button actions never
fired — a known flakiness of that modifier) with a native NSTouchBar
installed on the application object, the end of the Touch Bar responder
chain:

- Skip-back / play-pause / skip-forward as NSButtonTouchBarItems wired
  straight to the playback controller; the play icon and the artwork
  strip stay current through re-armed withObservationTracking.
- An NSScrubber strip of all subscriptions (artwork loaded and cached
  per podcast, swipe freely): tapping opens the podcast page, or plays
  the station directly for a radio, routed to RootView through the
  CommandCenter (openPodcastID).

Co-Authored-By: Claude (RCA)
parent 91ef1795
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
import Foundation
import Observation

/// Bridges menu-bar commands (Fichier › Ajouter…, ⌘N…) to the sheet state
/// owned by `RootView`, which binds its presentations to these flags.
/// Bridges menu-bar commands (Fichier › Ajouter…, ⌘N…) and the Touch Bar to
/// the state owned by `RootView`, which binds its presentations to these flags.
@MainActor
@Observable
final class CommandCenter {
    var showAddPodcast = false
    var showAddRadio = false
    var showOPMLImporter = false
    /// Podcast picked outside the view hierarchy (Touch Bar scrubber):
    /// RootView opens it (or plays it, for a radio) then clears the value.
    var openPodcastID: UUID?
}
+7 −0
Original line number Diff line number Diff line
@@ -10,8 +10,15 @@ import PlaybackKit
final class MacAppDelegate: NSObject, NSApplicationDelegate {
    /// Wired by the app once the playback controller exists.
    static weak var playback: PlaybackController?
    /// Keeps the Touch Bar controller (and its data sources) alive.
    private static var touchBarController: TouchBarController?
    private var spaceKeyMonitor: Any?

    static func installTouchBar(_ controller: TouchBarController) {
        touchBarController = controller
        controller.install()
    }

    func applicationDidFinishLaunching(_ notification: Notification) {
        spaceKeyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
            MainActor.assumeIsolated {

Skingomz/PlaybackTouchBar.swift

deleted100644 → 0
+0 −35
Original line number Diff line number Diff line
#if os(macOS)
import SwiftUI
import PlaybackKit

/// Transport controls shown on the MacBook Touch Bar while an episode is
/// loaded: skip back, play/pause, skip forward, plus the current title.
/// Attached to the root view; empty (system default bar) when idle.
struct PlaybackTouchBar: View {
    @Environment(PlaybackController.self) private var playback
    @Environment(PlaybackSettings.self) private var settings

    var body: some View {
        if let episode = playback.currentEpisode {
            Button {
                playback.skipBackward(TimeInterval(settings.skipBackward))
            } label: {
                Image(systemName: settings.backwardSymbol)
            }
            Button {
                playback.togglePlayPause()
            } label: {
                Image(systemName: playback.isPlaying ? "pause.fill" : "play.fill")
            }
            Button {
                playback.skipForward(TimeInterval(settings.skipForward))
            } label: {
                Image(systemName: settings.forwardSymbol)
            }
            Text(episode.title)
                .lineLimit(1)
                .frame(maxWidth: 280)
        }
    }
}
#endif
+9 −4
Original line number Diff line number Diff line
@@ -96,11 +96,16 @@ struct RootView: View {
            handleDroppedURLs(urls)
            return true
        }
        #if os(macOS)
        .touchBar {
            PlaybackTouchBar()
        // Touch Bar scrubber selection (macOS): open the podcast, play a radio.
        .onChange(of: commands.openPodcastID) { _, id in
            guard let id, let podcast = model.podcasts.first(where: { $0.id == id }) else { return }
            if podcast.isRadio {
                Task { await model.playStation(podcast) }
            } else {
                selection = .podcast(id)
            }
            commands.openPodcastID = nil
        }
        #endif
        .sheet(isPresented: $commands.showAddPodcast) { AddPodcastView() }
        .sheet(isPresented: $commands.showAddRadio) { AddRadioView() }
        .sheet(isPresented: $showingSearch) { SearchView() }
+4 −0
Original line number Diff line number Diff line
@@ -44,6 +44,10 @@ struct SkingomzApp: App {
                    nowPlaying = NowPlayingCoordinator(controller: playback, settings: playbackSettings)
                    #if os(macOS)
                    MacAppDelegate.playback = playback
                    MacAppDelegate.installTouchBar(
                        TouchBarController(model: model, playback: playback,
                                           settings: playbackSettings, commands: commandCenter)
                    )
                    #endif
                    await model.start()
                }
Loading