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), StemPlaylistPage(String) } /// 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) } } }