mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Move download audio option, clean up menus
This commit is contained in:
parent
fa99af3f6a
commit
6eab360da3
7 changed files with 175 additions and 155 deletions
|
|
@ -215,56 +215,118 @@ import Views
|
|||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
|
||||
var filtersHeader: some View {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack {
|
||||
if viewModel.searchTerm.count > 0 {
|
||||
TextChipButton.makeSearchFilterButton(title: viewModel.searchTerm) {
|
||||
viewModel.searchTerm = ""
|
||||
GeometryReader { reader in
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack {
|
||||
if viewModel.searchTerm.count > 0 {
|
||||
TextChipButton.makeSearchFilterButton(title: viewModel.searchTerm) {
|
||||
viewModel.searchTerm = ""
|
||||
}.frame(maxWidth: reader.size.width * 0.66)
|
||||
} else {
|
||||
Menu(
|
||||
content: {
|
||||
ForEach(LinkedItemFilter.allCases, id: \.self) { filter in
|
||||
Button(filter.displayName, action: { viewModel.appliedFilter = filter.rawValue })
|
||||
}
|
||||
},
|
||||
label: {
|
||||
TextChipButton.makeMenuButton(
|
||||
title: LinkedItemFilter(rawValue: viewModel.appliedFilter)?.displayName ?? "Filter"
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Menu(
|
||||
content: {
|
||||
ForEach(LinkedItemFilter.allCases, id: \.self) { filter in
|
||||
Button(filter.displayName, action: { viewModel.appliedFilter = filter.rawValue })
|
||||
ForEach(LinkedItemSort.allCases, id: \.self) { sort in
|
||||
Button(sort.displayName, action: { viewModel.appliedSort = sort.rawValue })
|
||||
}
|
||||
},
|
||||
label: {
|
||||
TextChipButton.makeMenuButton(
|
||||
title: LinkedItemFilter(rawValue: viewModel.appliedFilter)?.displayName ?? "Filter"
|
||||
title: LinkedItemSort(rawValue: viewModel.appliedSort)?.displayName ?? "Sort"
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
Menu(
|
||||
content: {
|
||||
ForEach(LinkedItemSort.allCases, id: \.self) { sort in
|
||||
Button(sort.displayName, action: { viewModel.appliedSort = sort.rawValue })
|
||||
TextChipButton.makeAddLabelButton {
|
||||
viewModel.showLabelsSheet = true
|
||||
}
|
||||
ForEach(viewModel.selectedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: false) {
|
||||
viewModel.selectedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
ForEach(viewModel.negatedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: true) {
|
||||
viewModel.negatedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(0)
|
||||
}
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
}
|
||||
|
||||
func menuItems(for item: LinkedItem) -> some View {
|
||||
Group {
|
||||
if (item.highlights?.count ?? 0) > 0 {
|
||||
Button(
|
||||
action: { viewModel.itemForHighlightsView = item },
|
||||
label: { Label("View Highlights & Notes", systemImage: "highlighter") }
|
||||
)
|
||||
}
|
||||
Button(
|
||||
action: { viewModel.itemUnderTitleEdit = item },
|
||||
label: { Label("Edit Title/Description", systemImage: "textbox") }
|
||||
)
|
||||
Button(
|
||||
action: { viewModel.itemUnderLabelEdit = item },
|
||||
label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") }
|
||||
)
|
||||
Button(action: {
|
||||
withAnimation(.linear(duration: 0.4)) {
|
||||
viewModel.setLinkArchived(
|
||||
dataService: dataService,
|
||||
objectID: item.objectID,
|
||||
archived: !item.isArchived
|
||||
)
|
||||
}
|
||||
}, label: {
|
||||
Label(
|
||||
item.isArchived ? "Unarchive" : "Archive",
|
||||
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
|
||||
)
|
||||
})
|
||||
Button(
|
||||
action: {
|
||||
itemToRemove = item
|
||||
confirmationShown = true
|
||||
},
|
||||
label: {
|
||||
Label("Remove Item", systemImage: "trash")
|
||||
}
|
||||
).tint(.red)
|
||||
if FeatureFlag.enableSnooze {
|
||||
Button {
|
||||
viewModel.itemToSnoozeID = item.id
|
||||
viewModel.snoozePresented = true
|
||||
} label: {
|
||||
Label { Text("Snooze") } icon: { Image.moon }
|
||||
}
|
||||
}
|
||||
if let author = item.author {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.searchTerm = "author:\"\(author)\""
|
||||
},
|
||||
label: {
|
||||
TextChipButton.makeMenuButton(
|
||||
title: LinkedItemSort(rawValue: viewModel.appliedSort)?.displayName ?? "Sort"
|
||||
)
|
||||
Label(String("More by \(author)"), systemImage: "person")
|
||||
}
|
||||
)
|
||||
TextChipButton.makeAddLabelButton {
|
||||
viewModel.showLabelsSheet = true
|
||||
}
|
||||
ForEach(viewModel.selectedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: false) {
|
||||
viewModel.selectedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
ForEach(viewModel.negatedLabels, id: \.self) { label in
|
||||
TextChipButton.makeRemovableLabelButton(feedItemLabel: label, negated: true) {
|
||||
viewModel.negatedLabels.removeAll { $0.id == label.id }
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(0)
|
||||
}
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
|
@ -292,59 +354,7 @@ import Views
|
|||
viewModel: viewModel
|
||||
)
|
||||
.contextMenu {
|
||||
Button(
|
||||
action: { viewModel.itemForHighlightsView = item },
|
||||
label: { Label("View Highlights & Notes", systemImage: "highlighter") }
|
||||
)
|
||||
Button(
|
||||
action: { viewModel.itemUnderTitleEdit = item },
|
||||
label: { Label("Edit Title/Description", systemImage: "textbox") }
|
||||
)
|
||||
Button(
|
||||
action: { viewModel.itemUnderLabelEdit = item },
|
||||
label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") }
|
||||
)
|
||||
Button(action: {
|
||||
withAnimation(.linear(duration: 0.4)) {
|
||||
viewModel.setLinkArchived(
|
||||
dataService: dataService,
|
||||
objectID: item.objectID,
|
||||
archived: !item.isArchived
|
||||
)
|
||||
}
|
||||
}, label: {
|
||||
Label(
|
||||
item.isArchived ? "Unarchive" : "Archive",
|
||||
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
|
||||
)
|
||||
})
|
||||
Button(
|
||||
action: {
|
||||
itemToRemove = item
|
||||
confirmationShown = true
|
||||
},
|
||||
label: {
|
||||
Label("Remove Item", systemImage: "trash")
|
||||
}
|
||||
).tint(.red)
|
||||
if FeatureFlag.enableSnooze {
|
||||
Button {
|
||||
viewModel.itemToSnoozeID = item.id
|
||||
viewModel.snoozePresented = true
|
||||
} label: {
|
||||
Label { Text("Snooze") } icon: { Image.moon }
|
||||
}
|
||||
}
|
||||
if let author = item.author {
|
||||
Button(
|
||||
action: {
|
||||
viewModel.searchTerm = "author:\"\(author)\""
|
||||
},
|
||||
label: {
|
||||
Label(String("More by \(author)"), systemImage: "person")
|
||||
}
|
||||
)
|
||||
}
|
||||
menuItems(for: item)
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
if !item.isArchived {
|
||||
|
|
@ -428,8 +438,6 @@ import Views
|
|||
viewModel.itemUnderLabelEdit = item
|
||||
case .editTitle:
|
||||
viewModel.itemUnderTitleEdit = item
|
||||
case .downloadAudio:
|
||||
viewModel.downloadAudio(audioController: audioController, item: item)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ import Views
|
|||
)
|
||||
Button(
|
||||
action: { viewModel.itemUnderLabelEdit = item },
|
||||
label: { Label("Edit Labels", systemImage: "tag") }
|
||||
label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") }
|
||||
)
|
||||
Button(action: {
|
||||
withAnimation(.linear(duration: 0.4)) {
|
||||
|
|
|
|||
|
|
@ -175,14 +175,6 @@ import Views
|
|||
showLoadingBar = false
|
||||
}
|
||||
|
||||
func downloadAudio(audioController: AudioController, item: LinkedItem) {
|
||||
Snackbar.show(message: "Downloading Offline Audio")
|
||||
Task {
|
||||
let downloaded = await audioController.downloadForOffline(itemID: item.unwrappedID)
|
||||
Snackbar.show(message: downloaded ? "Audio file downloaded" : "Error downloading audio")
|
||||
}
|
||||
}
|
||||
|
||||
private var fetchRequest: NSFetchRequest<Models.LinkedItem> {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItem> = LinkedItem.fetchRequest()
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,60 @@ struct WebReaderContainerView: View {
|
|||
}.foregroundColor(.appGrayTextContrast)
|
||||
}
|
||||
|
||||
func menuItems(for item: LinkedItem) -> some View {
|
||||
let hasLabels = item.labels?.count == 0
|
||||
let hasHighlights = (item.highlights?.count ?? 0) > 0
|
||||
return Group {
|
||||
if hasHighlights {
|
||||
Button(
|
||||
action: { showHighlightsView = true },
|
||||
label: { Label("View Highlights & Notes", systemImage: "highlighter") }
|
||||
)
|
||||
}
|
||||
Button(
|
||||
action: { showTitleEdit = true },
|
||||
label: { Label("Edit Title/Description", systemImage: "textbox") }
|
||||
)
|
||||
Button(
|
||||
action: editLabels,
|
||||
label: { Label(hasLabels ? "Edit Labels" : "Add Labels", systemImage: "tag") }
|
||||
)
|
||||
Button(
|
||||
action: {
|
||||
archive()
|
||||
},
|
||||
label: {
|
||||
Label(
|
||||
item.isArchived ? "Unarchive" : "Archive",
|
||||
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
|
||||
)
|
||||
}
|
||||
)
|
||||
Button(
|
||||
action: {
|
||||
dataService.updateLinkReadingProgress(itemID: item.unwrappedID, readingProgress: 0, anchorIndex: 0)
|
||||
},
|
||||
label: { Label("Reset Read Location", systemImage: "arrow.counterclockwise.circle") }
|
||||
)
|
||||
Button(
|
||||
action: {
|
||||
viewModel.downloadAudio(audioController: audioController, item: item)
|
||||
},
|
||||
label: { Label("Download Audio", systemImage: "icloud.and.arrow.down") }
|
||||
)
|
||||
if viewModel.hasOriginalUrl(item) {
|
||||
Button(
|
||||
action: share,
|
||||
label: { Label("Share Original", systemImage: "square.and.arrow.up") }
|
||||
)
|
||||
}
|
||||
Button(
|
||||
action: delete,
|
||||
label: { Label("Delete", systemImage: "trash") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var navBar: some View {
|
||||
HStack(alignment: .center) {
|
||||
#if os(iOS)
|
||||
|
|
@ -183,55 +237,7 @@ struct WebReaderContainerView: View {
|
|||
#endif
|
||||
Menu(
|
||||
content: {
|
||||
Group {
|
||||
Button(
|
||||
action: { showHighlightsView = true },
|
||||
label: { Label("View Highlights & Notes", systemImage: "highlighter") }
|
||||
)
|
||||
Button(
|
||||
action: { showTitleEdit = true },
|
||||
label: { Label("Edit Title/Description", systemImage: "textbox") }
|
||||
)
|
||||
Button(
|
||||
action: editLabels,
|
||||
label: { Label("Edit Labels", systemImage: "tag") }
|
||||
)
|
||||
Button(
|
||||
action: {
|
||||
archive()
|
||||
},
|
||||
label: {
|
||||
Label(
|
||||
item.isArchived ? "Unarchive" : "Archive",
|
||||
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
|
||||
)
|
||||
}
|
||||
)
|
||||
Button(
|
||||
action: {
|
||||
dataService.updateLinkReadingProgress(itemID: item.unwrappedID, readingProgress: 0, anchorIndex: 0)
|
||||
},
|
||||
label: { Label("Reset Read Location", systemImage: "arrow.counterclockwise.circle") }
|
||||
)
|
||||
Button(
|
||||
action: { /* viewModel.downloadAudio(audioController: audioController, item: item) */ },
|
||||
label: { Label("Download Audio", systemImage: "icloud.and.arrow.down") }
|
||||
)
|
||||
Button(
|
||||
action: share,
|
||||
label: { Label("Share Original", systemImage: "square.and.arrow.up") }
|
||||
)
|
||||
if viewModel.hasOriginalUrl(item) {
|
||||
Button(
|
||||
action: share,
|
||||
label: { Label("Share Original", systemImage: "square.and.arrow.up") }
|
||||
)
|
||||
}
|
||||
Button(
|
||||
action: delete,
|
||||
label: { Label("Delete", systemImage: "trash") }
|
||||
)
|
||||
}
|
||||
menuItems(for: item)
|
||||
},
|
||||
label: {
|
||||
#if os(iOS)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Views
|
||||
import WebKit
|
||||
|
||||
struct SafariWebLink: Identifiable {
|
||||
|
|
@ -22,6 +23,14 @@ struct SafariWebLink: Identifiable {
|
|||
return false
|
||||
}
|
||||
|
||||
func downloadAudio(audioController: AudioController, item: LinkedItem) {
|
||||
Snackbar.show(message: "Downloading Offline Audio")
|
||||
Task {
|
||||
let downloaded = await audioController.downloadForOffline(itemID: item.unwrappedID)
|
||||
Snackbar.show(message: downloaded ? "Audio file downloaded" : "Error downloading audio")
|
||||
}
|
||||
}
|
||||
|
||||
func loadContent(dataService: DataService, username: String, itemID: String, retryCount: Int = 0) async {
|
||||
errorMessage = nil
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ public enum GridCardAction {
|
|||
case delete
|
||||
case editLabels
|
||||
case editTitle
|
||||
case downloadAudio
|
||||
case viewHighlights
|
||||
}
|
||||
|
||||
|
|
@ -46,17 +45,19 @@ public struct GridCard: View {
|
|||
|
||||
var contextMenuView: some View {
|
||||
Group {
|
||||
Button(
|
||||
action: { menuActionHandler(.viewHighlights) },
|
||||
label: { Label("View Highlights & Notes", systemImage: "highlighter") }
|
||||
)
|
||||
if (item.highlights?.count ?? 0) > 0 {
|
||||
Button(
|
||||
action: { menuActionHandler(.viewHighlights) },
|
||||
label: { Label("View Highlights & Notes", systemImage: "highlighter") }
|
||||
)
|
||||
}
|
||||
Button(
|
||||
action: { menuActionHandler(.editTitle) },
|
||||
label: { Label("Edit Title/Description", systemImage: "textbox") }
|
||||
)
|
||||
Button(
|
||||
action: { menuActionHandler(.editLabels) },
|
||||
label: { Label("Edit Labels", systemImage: "tag") }
|
||||
label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") }
|
||||
)
|
||||
Button(
|
||||
action: { menuActionHandler(.toggleArchiveStatus) },
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public struct TextChipButton: View {
|
|||
}
|
||||
|
||||
public static func makeSearchFilterButton(title: String, onTap: @escaping () -> Void) -> TextChipButton {
|
||||
TextChipButton(title: "Search: \(title)", color: .appCtaYellow, actionType: .clear, negated: false, onTap: onTap)
|
||||
TextChipButton(title: title, color: .appCtaYellow, actionType: .filter, negated: false, onTap: onTap)
|
||||
}
|
||||
|
||||
public static func makeShowOptionsButton(title: String, onTap: @escaping () -> Void) -> TextChipButton {
|
||||
|
|
@ -135,11 +135,11 @@ public struct TextChipButton: View {
|
|||
case remove
|
||||
case add
|
||||
case show
|
||||
case clear
|
||||
case filter
|
||||
|
||||
var systemIconName: String {
|
||||
switch self {
|
||||
case .clear, .remove:
|
||||
case .filter, .remove:
|
||||
return "xmark"
|
||||
case .add:
|
||||
return "plus"
|
||||
|
|
@ -173,6 +173,10 @@ public struct TextChipButton: View {
|
|||
public var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
if actionType == .filter {
|
||||
Image(systemName: "line.3.horizontal.decrease")
|
||||
}
|
||||
|
||||
Text(text)
|
||||
.strikethrough(color: negated ? foregroundColor : .clear)
|
||||
.padding(.leading, 3)
|
||||
|
|
|
|||
Loading…
Reference in a new issue