Add iOS app with CarPlay and Siri
SwiftUI port of the Android app under ios/, with CarPlay browse and Now Playing, Siri play-media intents, and a build-ipa.sh script that makes an unsigned IPA for sideloading. README gets an iOS section and the vibecoded disclaimer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
45bc0eefe0
commit
fdf9e970b2
45 files changed
+9822
No files matched your search
@@ -0,0 +1,65 @@
|
||||
import SwiftUI
|
||||
|
||||
enum Tab: String, CaseIterable, Hashable {
|
||||
case Library, Playlists, Search, Downloads, Settings
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .Library: "square.stack.fill"
|
||||
case .Playlists: "music.note.list"
|
||||
case .Search: "magnifyingglass"
|
||||
case .Downloads: "arrow.down.circle.fill"
|
||||
case .Settings: "gearshape.fill"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Route: Hashable {
|
||||
case Artists, Albums, Songs, Playlists
|
||||
case Eq, EqFilters(profileId: String), EqFilterPage(profileId: String, filterId: String)
|
||||
case PlexAccount, StemServer, Quality, Storage, Appearance
|
||||
case ArtistPage(Artist), AlbumPage(Album), PlaylistPage(Playlist)
|
||||
}
|
||||
|
||||
/// Tabs, their stacks and the Now Playing sheet: the things any screen can ask of the shell.
|
||||
@MainActor
|
||||
@Observable
|
||||
final class Ui {
|
||||
var tab: Tab = .Library
|
||||
var paths: [Tab: [Route]] = [:]
|
||||
var nowPlayingOpen = false {
|
||||
didSet { overlays.level = nowPlayingOpen ? 1 : 0 }
|
||||
}
|
||||
let overlays: Overlays
|
||||
|
||||
init(overlays: Overlays) { self.overlays = overlays }
|
||||
|
||||
func path(_ t: Tab) -> Binding<[Route]> {
|
||||
Binding(get: { self.paths[t] ?? [] }, set: { self.paths[t] = $0 })
|
||||
}
|
||||
|
||||
func push(_ r: Route) { paths[tab, default: []].append(r) }
|
||||
|
||||
func pop() { if paths[tab]?.isEmpty == false { paths[tab]?.removeLast() } }
|
||||
|
||||
/// Tapping the selected tab again pops it to its root.
|
||||
func selectTab(_ t: Tab) {
|
||||
if t == tab { paths[t] = [] } else { tab = t }
|
||||
}
|
||||
|
||||
func openNowPlaying() { nowPlayingOpen = true }
|
||||
|
||||
/// Switches to the Library tab and pushes [r] there.
|
||||
func showInLibrary(_ r: Route) {
|
||||
nowPlayingOpen = false
|
||||
tab = .Library
|
||||
paths[.Library, default: []].append(r)
|
||||
}
|
||||
|
||||
/// Switches to Settings and opens [r] (the Equalizer) there.
|
||||
func showInSettings(_ r: Route) {
|
||||
nowPlayingOpen = false
|
||||
tab = .Settings
|
||||
if paths[.Settings]?.last != r { paths[.Settings, default: []].append(r) }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user