Turns the Podcasts tab into a proper podcast-app experience across three features that compose on a shared background-generation service. Background generation (#2) - New PodcastGenerationManager runs generate→poll→download off the player, so producing a new episode never stops current playback. - Headphone taps never interrupt: idle → generate-and-play in the foreground (unchanged); already playing → generate in the background, episode lands in the library. A "Generating…" banner surfaces active jobs. Played/unplayed + queue (#1) - PodcastEntry gains playedAt (old index.json decodes as unplayed). - Podcasts tab splits into Up Next / Played with a Play All queue that auto-advances; finishing marks played; swipe to toggle played state. Share-extension audio (#3) - "Create podcast" toggle on the save card, plus configurable Auto-Podcast Tags in Settings. The extension enqueues a request into the App Group; the main app drains it on launch/foreground and generates in the background. Closes #1 Closes #2 Closes #3 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
1.9 KiB
Swift
54 lines
1.9 KiB
Swift
import SwiftUI
|
|
|
|
struct SettingsView: View {
|
|
@Bindable var viewModel: BookmarksViewModel
|
|
let onDisconnect: () -> Void
|
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
@State private var autoTagText = PodcastRequests.autoTags.joined(separator: " ")
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List {
|
|
Section("AI Features") {
|
|
Label("Semantic search, auto-tagging, smart collections, and podcast generation are active.", systemImage: "sparkles")
|
|
.font(.system(size: 13))
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
Section {
|
|
TextField("e.g. listen podcast", text: $autoTagText)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
.onChange(of: autoTagText) { _, new in
|
|
PodcastRequests.autoTags = new
|
|
.split(whereSeparator: { $0 == " " || $0 == "," })
|
|
.map(String.init)
|
|
}
|
|
} header: {
|
|
Text("Auto-Podcast Tags")
|
|
} footer: {
|
|
Text("Saving a bookmark with any of these tags automatically generates a podcast for it.")
|
|
}
|
|
|
|
Section("Server") {
|
|
Button(role: .destructive) {
|
|
dismiss()
|
|
onDisconnect()
|
|
} label: {
|
|
Label("Disconnect", systemImage: "person.crop.circle.badge.minus")
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Settings")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button("Done") { dismiss() }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|