import SwiftUI /// The native interface: feeds beside items, with the player bar under both. /// /// What is not here yet has somewhere to go rather than being missing. The item pane is #4 and /// the management screens are #5; until then the toolbar's Page button opens ipx's own page, /// which is still the whole app in a web view and can do everything this cannot. struct LibraryView: View { @ObservedObject var store: LibraryStore @ObservedObject var playback: Playback /// The file is the one asked for, or the item's first playable one. var play: (IPX.Entry, IPX.Enclosure?) -> Void var openPage: (WebViewController.Screen) -> Void @ObservedObject var keys: RootViewController.Flag @ObservedObject var sidebar: RootViewController.Flag /// Everything that lives in the page, named rather than hidden behind one button: it should /// be as easy to reach Settings here as it is in a browser. private var pageMenu: some View { Menu { Button { openPage(.settings) } label: { Label("Settings", systemImage: "gearshape") } Button { openPage(.directory) } label: { Label("Directory", systemImage: "square.grid.2x2") } Button { openPage(.popular) } label: { Label("Popular", systemImage: "star") } Button { openPage(.opml) } label: { Label("Import or export OPML", systemImage: "square.and.arrow.up") } if store.isAdmin { Divider() Button { openPage(.admin) } label: { Label("Admin", systemImage: "wrench.and.screwdriver") } } Divider() Button { openPage(.page) } label: { Label("Open the full page", systemImage: "safari") } } label: { Image(systemName: "ellipsis.circle") } .accessibilityLabel("More") .help("Settings, the directory, OPML") } @Environment(\.horizontalSizeClass) private var width private var wide: Bool { width != .compact } var body: some View { NavigationSplitView(columnVisibility: Binding( get: { sidebar.on ? .detailOnly : .all }, set: { sidebar.on = $0 == .detailOnly })) { FeedListView(store: store) } detail: { // Wide enough for the page's own three-pane arrangement, or the phone's one-at-a- // time. The page draws the same line at 820px and hides its columns below it. Group { if wide { ContentPaneView(store: store, playback: playback, play: play) } else { ItemListView(store: store, playback: playback, play: play) } } .toolbar { ToolbarItem(placement: .primaryAction) { pageMenu } } } // The wash belongs to the window, once. A copy behind each pane met at the split and // the join showed as a seam straight down the middle. .background(Glass.Wash()) .tint(Glass.accent) .navigationSplitViewColumnWidth(min: 200, ideal: 260) .sheet(isPresented: $keys.on) { KeysView() } .alert("That did not save", isPresented: Binding(get: { store.problem != nil }, set: { if !$0 { store.problem = nil } })) { Button("OK", role: .cancel) { store.problem = nil } } message: { Text(store.problem ?? "") } .task { await store.refreshFeeds() store.reload() } .safeAreaInset(edge: .bottom, spacing: 0) { PlayerBarView(playback: playback) } } }