mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Work on multi tab iOS client
This commit is contained in:
parent
7cf0c37c88
commit
2d021c2683
6 changed files with 156 additions and 42 deletions
|
|
@ -430,7 +430,9 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
}
|
||||
|
||||
List {
|
||||
if !viewModel.hideFeatureSection, viewModel.items.count > 0,
|
||||
if viewModel.listConfig.hasFeatureCards,
|
||||
!viewModel.hideFeatureSection,
|
||||
viewModel.items.count > 0,
|
||||
viewModel.searchTerm.isEmpty, viewModel.selectedLabels.isEmpty,
|
||||
viewModel.negatedLabels.isEmpty
|
||||
{
|
||||
|
|
@ -449,41 +451,16 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
.contextMenu {
|
||||
menuItems(for: item)
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
if !item.isArchived {
|
||||
Button(action: {
|
||||
withAnimation(.linear(duration: 0.4)) {
|
||||
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: true)
|
||||
}
|
||||
}, label: {
|
||||
Label("Archive", systemImage: "archivebox")
|
||||
}).tint(.green)
|
||||
} else {
|
||||
Button(action: {
|
||||
withAnimation(.linear(duration: 0.4)) {
|
||||
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: false)
|
||||
}
|
||||
}, label: {
|
||||
Label("Unarchive", systemImage: "tray.and.arrow.down.fill")
|
||||
}).tint(.indigo)
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
ForEach(viewModel.listConfig.leadingSwipeActions, id: \.self) { action in
|
||||
swipeActionButton(action: action, item: item)
|
||||
}
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
ForEach(viewModel.listConfig.trailingSwipeActions, id: \.self) { action in
|
||||
swipeActionButton(action: action, item: item)
|
||||
}
|
||||
Button(
|
||||
action: {
|
||||
itemToRemove = item
|
||||
confirmationShown = true
|
||||
},
|
||||
label: {
|
||||
Image(systemName: "trash")
|
||||
}
|
||||
).tint(.red)
|
||||
}
|
||||
// .swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
// Button {
|
||||
// viewModel.pinItem(dataService, item)
|
||||
// } label: {
|
||||
// Label { Text("Pin") } icon: { Image(systemName: "pin.fill") }
|
||||
// }.tint(Color(hex: "#0A84FF"))
|
||||
// }
|
||||
}
|
||||
}
|
||||
.padding(0)
|
||||
|
|
@ -510,6 +487,50 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
Button(LocalText.cancelGeneric, role: .cancel) { self.showHideFeatureAlert = false }
|
||||
}
|
||||
}
|
||||
|
||||
func swipeActionButton(action: SwipeAction, item: LinkedItem) -> AnyView {
|
||||
switch action {
|
||||
case .pin:
|
||||
return AnyView(Button(action: {
|
||||
viewModel.addLabel(dataService: dataService, item: item, label: "Pinned")
|
||||
}, label: {
|
||||
VStack {
|
||||
Image(systemName: "pin.fill")
|
||||
.rotationEffect(Angle(degrees: 90))
|
||||
Text("Pin")
|
||||
}
|
||||
}).tint(Color(hex: "#0A84FF")))
|
||||
case .archive:
|
||||
return AnyView(Button(action: {
|
||||
withAnimation(.linear(duration: 0.4)) {
|
||||
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: !item.isArchived)
|
||||
}
|
||||
}, label: {
|
||||
Label(!item.isArchived ? "Archive" : "Unarchive", systemImage: !item.isArchived ? "archivebox" : "tray.and.arrow.down.fill")
|
||||
})
|
||||
.tint(!item.isArchived ? .green : .indigo))
|
||||
case .delete:
|
||||
return AnyView(Button(
|
||||
action: {
|
||||
itemToRemove = item
|
||||
confirmationShown = true
|
||||
},
|
||||
label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
).tint(.red))
|
||||
case .moveToInbox:
|
||||
return AnyView(Button(
|
||||
action: {
|
||||
viewModel.addLabel(dataService: dataService, item: item, label: "Inbox")
|
||||
|
||||
},
|
||||
label: {
|
||||
Label("Move to Inbox", systemImage: "tray.fill")
|
||||
}
|
||||
).tint(Color(hex: "#0A84FF")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HomeFeedGridView: View {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ import Views
|
|||
@Published var showCommunityModal = false
|
||||
@Published var featureItems = [LinkedItem]()
|
||||
|
||||
@Published var listConfig: LibraryListConfig
|
||||
|
||||
var cursor: String?
|
||||
|
||||
// These are used to make sure we handle search result
|
||||
|
|
@ -48,6 +50,11 @@ import Views
|
|||
@AppStorage(UserDefaultKey.lastSelectedLinkedItemFilter.rawValue) var appliedFilter = LinkedItemFilter.inbox.rawValue
|
||||
@AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue
|
||||
|
||||
init(listConfig: LibraryListConfig) {
|
||||
self.listConfig = listConfig
|
||||
super.init()
|
||||
}
|
||||
|
||||
func setItems(_ items: [LinkedItem]) {
|
||||
self.items = items
|
||||
}
|
||||
|
|
@ -56,6 +63,7 @@ import Views
|
|||
Task {
|
||||
if let filter = filter {
|
||||
featureItems = await loadFeatureItems(dataService: dataService, predicate: filter.predicate, sort: filter.sortDescriptor)
|
||||
print("updated feature items: ", featureItems.count)
|
||||
} else {
|
||||
featureItems = []
|
||||
}
|
||||
|
|
@ -247,8 +255,6 @@ import Views
|
|||
showLoadingBar = false
|
||||
}
|
||||
|
||||
func pinItem(dataService _: DataService, item _: LinkedItem) async {}
|
||||
|
||||
func loadFeatureItems(dataService: DataService, predicate: NSPredicate, sort: NSSortDescriptor) async -> [LinkedItem] {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItem> = LinkedItem.fetchRequest()
|
||||
fetchRequest.fetchLimit = 25
|
||||
|
|
@ -322,6 +328,15 @@ import Views
|
|||
dataService.removeLink(objectID: objectID)
|
||||
}
|
||||
|
||||
func addLabel(dataService: DataService, item: LinkedItem, label: String) {
|
||||
Task {
|
||||
if let label = LinkedItemLabel.named(label, inContext: dataService.viewContext) {
|
||||
dataService.updateItemLabels(itemID: item.unwrappedID, labelIDs: [label.unwrappedID])
|
||||
}
|
||||
updateFeatureFilter(dataService: dataService, filter: FeaturedItemFilter(rawValue: featureFilter))
|
||||
}
|
||||
}
|
||||
|
||||
func snoozeUntil(dataService: DataService, linkId: String, until: Date, successMessage: String?) async {
|
||||
isLoading = true
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Jackson Harper on 7/1/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum CardStyle {
|
||||
case library
|
||||
case highlights
|
||||
}
|
||||
|
||||
struct LibraryListConfig {
|
||||
var hasFeatureCards = false
|
||||
var leadingSwipeActions = [SwipeAction]()
|
||||
var trailingSwipeActions = [SwipeAction]()
|
||||
var cardStyle = CardStyle.library
|
||||
}
|
||||
18
apple/OmnivoreKit/Sources/App/Views/Home/SwipeAction.swift
Normal file
18
apple/OmnivoreKit/Sources/App/Views/Home/SwipeAction.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// SwipeAction.swift
|
||||
//
|
||||
//
|
||||
// Created by Jackson Harper on 7/1/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
|
||||
enum SwipeAction: String, CaseIterable {
|
||||
case pin
|
||||
case archive
|
||||
case delete
|
||||
case moveToInbox
|
||||
}
|
||||
|
|
@ -10,19 +10,44 @@ import Models
|
|||
import SwiftUI
|
||||
|
||||
struct LibraryTabView: View {
|
||||
@StateObject private var viewModel = HomeFeedViewModel()
|
||||
@StateObject private var subViewModel = HomeFeedViewModel(
|
||||
listConfig: LibraryListConfig(
|
||||
hasFeatureCards: false,
|
||||
leadingSwipeActions: [.moveToInbox],
|
||||
trailingSwipeActions: [.delete, .archive],
|
||||
cardStyle: .library
|
||||
)
|
||||
)
|
||||
|
||||
@StateObject private var libraryViewModel = HomeFeedViewModel(
|
||||
listConfig: LibraryListConfig(
|
||||
hasFeatureCards: true,
|
||||
leadingSwipeActions: [.pin],
|
||||
trailingSwipeActions: [.delete, .archive],
|
||||
cardStyle: .library
|
||||
)
|
||||
)
|
||||
|
||||
@StateObject private var highlightsViewModel = HomeFeedViewModel(
|
||||
listConfig: LibraryListConfig(
|
||||
hasFeatureCards: true,
|
||||
leadingSwipeActions: [.pin],
|
||||
trailingSwipeActions: [.delete, .archive],
|
||||
cardStyle: .highlights
|
||||
)
|
||||
)
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ZStack {
|
||||
NavigationLink(
|
||||
destination: LinkDestination(selectedItem: viewModel.selectedItem),
|
||||
isActive: $viewModel.linkIsActive
|
||||
destination: LinkDestination(selectedItem: libraryViewModel.selectedItem),
|
||||
isActive: $libraryViewModel.linkIsActive
|
||||
) {
|
||||
EmptyView()
|
||||
}
|
||||
TabView {
|
||||
HomeView(viewModel: viewModel)
|
||||
HomeView(viewModel: subViewModel)
|
||||
.tabItem {
|
||||
Label {
|
||||
Text("Subscriptions")
|
||||
|
|
@ -30,7 +55,7 @@ struct LibraryTabView: View {
|
|||
Image.tabSubscriptions
|
||||
}
|
||||
}
|
||||
HomeView(viewModel: viewModel)
|
||||
HomeView(viewModel: libraryViewModel)
|
||||
.tabItem {
|
||||
Label {
|
||||
Text("Library")
|
||||
|
|
@ -38,7 +63,7 @@ struct LibraryTabView: View {
|
|||
Image.tabLibrary
|
||||
}
|
||||
}
|
||||
HomeView(viewModel: viewModel)
|
||||
HomeView(viewModel: highlightsViewModel)
|
||||
.tabItem {
|
||||
Label {
|
||||
Text("Highlights")
|
||||
|
|
|
|||
|
|
@ -60,6 +60,21 @@ extension LinkedItemLabel {
|
|||
public var unwrappedID: String { id ?? "" }
|
||||
public var unwrappedName: String { name ?? "" }
|
||||
|
||||
public static func named(_ name: String, inContext context: NSManagedObjectContext) -> LinkedItemLabel? {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItemLabel> = LinkedItemLabel.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(
|
||||
format: "name == %@", name
|
||||
)
|
||||
|
||||
var label: LinkedItemLabel?
|
||||
|
||||
context.performAndWait {
|
||||
label = (try? context.fetch(fetchRequest))?.first
|
||||
}
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
static func lookup(byID id: String, inContext context: NSManagedObjectContext) -> LinkedItemLabel? {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItemLabel> = LinkedItemLabel.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(
|
||||
|
|
|
|||
Loading…
Reference in a new issue