Add mini-player, per-row podcast button, and resume-from-position
Some checks failed
CI / build-and-deploy (push) Failing after 9s
Some checks failed
CI / build-and-deploy (push) Failing after 9s
- MiniPlayerView: persistent bar above tab bar, shows title/progress, play/pause and close buttons, slides in when audio starts - PodcastPlayerViewModel lifted to BookmarksViewModel so it persists across sheet dismissals — audio keeps playing in background - Headphone button on every bookmark row (filled = cached, outline = not) - start() is idempotent: re-tapping same article resumes without restart - stop() saves playback position before teardown (PodcastProgress) - setupPlayer restores saved position on next open - BrowserView keeps its own local VM (stopOnDismiss: true) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,17 +10,28 @@ struct BookmarksView: View {
|
||||
@State private var showAddBookmark = false
|
||||
@State private var editingBookmark: Bookmark?
|
||||
@State private var browsingBookmark: Bookmark?
|
||||
@State private var podcastBookmark: Bookmark?
|
||||
@State private var showFullPlayer = false
|
||||
@State private var readingProgress: [String: Double] = ReadingProgress.all()
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
ForEach(viewModel.bookmarks) { bookmark in
|
||||
BookmarkRow(bookmark: bookmark, readingProgress: readingProgress[bookmark.url] ?? 0)
|
||||
.onTapGesture {
|
||||
browsingBookmark = bookmark
|
||||
BookmarkRow(
|
||||
bookmark: bookmark,
|
||||
readingProgress: readingProgress[bookmark.url] ?? 0,
|
||||
onPodcast: {
|
||||
viewModel.podcastPlayer.start(
|
||||
articleUrl: bookmark.url,
|
||||
articleTitle: bookmark.displayTitle,
|
||||
claude: viewModel.claude
|
||||
)
|
||||
showFullPlayer = true
|
||||
}
|
||||
)
|
||||
.onTapGesture {
|
||||
browsingBookmark = bookmark
|
||||
}
|
||||
.onAppear { maybeLoadMore(bookmark) }
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20))
|
||||
.listRowSeparator(.visible)
|
||||
@@ -62,7 +73,12 @@ struct BookmarksView: View {
|
||||
Label("Open in Safari", systemImage: "safari")
|
||||
}
|
||||
Button {
|
||||
podcastBookmark = bookmark
|
||||
viewModel.podcastPlayer.start(
|
||||
articleUrl: bookmark.url,
|
||||
articleTitle: bookmark.displayTitle,
|
||||
claude: viewModel.claude
|
||||
)
|
||||
showFullPlayer = true
|
||||
} label: {
|
||||
Label("Convert to Podcast", systemImage: "headphones")
|
||||
}
|
||||
@@ -133,9 +149,19 @@ struct BookmarksView: View {
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .bottom) {
|
||||
if viewModel.enrichmentProgress > 0 {
|
||||
enrichmentBanner
|
||||
VStack(spacing: 8) {
|
||||
if viewModel.podcastPlayer.player != nil {
|
||||
MiniPlayerView(vm: viewModel.podcastPlayer) {
|
||||
showFullPlayer = true
|
||||
}
|
||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
||||
}
|
||||
if viewModel.enrichmentProgress > 0 {
|
||||
enrichmentBanner
|
||||
}
|
||||
}
|
||||
.animation(.spring(duration: 0.3), value: viewModel.podcastPlayer.player != nil)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showSettings) {
|
||||
@@ -158,8 +184,14 @@ struct BookmarksView: View {
|
||||
.onChange(of: browsingBookmark) { _, new in
|
||||
if new == nil { readingProgress = ReadingProgress.all() }
|
||||
}
|
||||
.sheet(item: $podcastBookmark) { bookmark in
|
||||
PodcastPlayerView(articleUrl: bookmark.url, claude: viewModel.claude)
|
||||
.sheet(isPresented: $showFullPlayer) {
|
||||
PodcastPlayerView(
|
||||
vm: viewModel.podcastPlayer,
|
||||
articleUrl: viewModel.podcastPlayer.currentArticleUrl,
|
||||
articleTitle: viewModel.podcastPlayer.currentArticleTitle,
|
||||
claude: viewModel.claude,
|
||||
stopOnDismiss: false
|
||||
)
|
||||
}
|
||||
.refreshable {
|
||||
await viewModel.load()
|
||||
@@ -193,7 +225,6 @@ struct BookmarksView: View {
|
||||
.background(.regularMaterial)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
private func maybeLoadMore(_ bookmark: Bookmark) {
|
||||
|
||||
Reference in New Issue
Block a user