The reading pane: the item, its files with play, download and delete, read and pin in the toolbar, a link to the original, and the notes. The notes are a web view, deliberately. They are feed-supplied HTML that ipx has already run through ammonia, and there is no good native renderer for that -- NSAttributedString(html:) is slow, single-threaded and ugly, and writing a real one is a project. So this carries only the notes, with just enough stylesheet to belong to the app: the system font at body size, the label colours, links in the accent. Nothing else is imposed, because the markup is the publisher's. A link opens in Safari rather than inside the view, where it would replace the notes with somebody's website and leave no way back. Deleting asks first, and says when the file is shared: one file serves everyone reading the feed, so removing it is not a private act. Two goes at the navigation. A row with a selection binding on the list highlighted and went nowhere -- the binding takes the tap before the link sees it. Without the binding, NavigationLink(value:) with a matching navigationDestination still pushed nothing and gave no reason, so the link carries its own destination now, which has nothing to get wrong. Fourteen tests pass. The new one opens an item and looks for its notes, its delete button and its read control, so a pane that renders empty fails rather than passing quietly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
38 lines
1.4 KiB
Swift
38 lines
1.4 KiB
Swift
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: () -> Void
|
|
|
|
var body: some View {
|
|
NavigationSplitView {
|
|
FeedListView(store: store)
|
|
.background(Glass.Wash())
|
|
} detail: {
|
|
ItemListView(store: store, playback: playback, play: play)
|
|
.toolbar {
|
|
ToolbarItem(placement: .primaryAction) {
|
|
Button(action: openPage) { Image(systemName: "safari") }
|
|
.help("Open the full page: settings, the directory, OPML")
|
|
}
|
|
}
|
|
}
|
|
.tint(Glass.accent)
|
|
.task {
|
|
await store.refreshFeeds()
|
|
store.reload()
|
|
}
|
|
.safeAreaInset(edge: .bottom, spacing: 0) {
|
|
PlayerBarView(playback: playback)
|
|
}
|
|
}
|
|
}
|