import SwiftUI /// Full-featured list row used by BookmarksView, TagBookmarksView, and SearchView. /// Owns podcast and episode-picker sheet state; parent owns BrowserView sheet. struct BookmarkListRow: View { let bookmark: Bookmark let viewModel: BookmarksViewModel var readingProgress: Double = 0 let onOpen: () -> Void var onEdit: (() -> Void)? = nil @Environment(\.openURL) private var openURL @State private var showFullPlayer = false @State private var episodePickerBookmark: Bookmark? var body: some View { BookmarkRow( bookmark: bookmark, readingProgress: readingProgress, onPodcast: handlePodcast ) .contentShape(Rectangle()) .onTapGesture { onOpen() } .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) .listRowSeparator(.visible) // Delete is destructive and not undoable — require an explicit tap on the // revealed button rather than letting a single full swipe delete instantly. .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button(role: .destructive) { Task { await viewModel.delete(bookmark) } } label: { Label("Delete", systemImage: "trash") } } .swipeActions(edge: .leading) { Button { Task { await viewModel.archive(bookmark) } } label: { Label("Archive", systemImage: "archivebox") } .tint(.orange) if let onEdit { Button { onEdit() } label: { Label("Edit", systemImage: "pencil") } .tint(.blue) } } .contextMenu { if let onEdit { Button { onEdit() } label: { Label("Edit", systemImage: "pencil") } } Button { onOpen() } label: { Label("Open", systemImage: "globe") } Button { if let url = URL(string: bookmark.url) { openURL(url) } } label: { Label("Open in Safari", systemImage: "safari") } Button { handlePodcast() } label: { Label("Convert to Podcast", systemImage: "headphones") } Divider() Button { Task { await viewModel.archive(bookmark) } } label: { Label("Archive", systemImage: "archivebox") } Button(role: .destructive) { Task { await viewModel.delete(bookmark) } } label: { Label("Delete", systemImage: "trash") } } .sheet(isPresented: $showFullPlayer) { PodcastPlayerView( vm: viewModel.podcastPlayer, articleUrl: viewModel.podcastPlayer.currentArticleUrl, articleTitle: viewModel.podcastPlayer.currentArticleTitle, claude: viewModel.claude, stopOnDismiss: false ) } .sheet(item: $episodePickerBookmark) { b in EpisodePickerView(bookmark: b, vm: viewModel.podcastPlayer, claude: viewModel.claude) } } private func handlePodcast() { let episodes = PodcastIndex.find(for: bookmark.url) if episodes.count >= 2 { episodePickerBookmark = bookmark } else if let ep = episodes.first { viewModel.podcastPlayer.start( articleUrl: ep.articleUrl, articleTitle: ep.title ?? bookmark.displayTitle, claude: viewModel.claude ) showFullPlayer = true } else { viewModel.podcastPlayer.start( articleUrl: bookmark.url, articleTitle: bookmark.displayTitle, claude: viewModel.claude ) showFullPlayer = true } } }