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 { bookmarkMenuItems( bookmark: bookmark, viewModel: viewModel, openURL: openURL, onOpen: onOpen, onEdit: onEdit, onPodcast: handlePodcast ) } .podcastSheets( viewModel: viewModel, showFullPlayer: $showFullPlayer, episodePicker: $episodePickerBookmark ) } private func handlePodcast() { launchPodcast( for: bookmark, viewModel: viewModel, showFullPlayer: $showFullPlayer, episodePicker: $episodePickerBookmark ) } }