Merge pull request #1187 from omnivore-app/fix/recently-read-nav-pop

Fix: iOS Nav Pop
This commit is contained in:
Jackson Harper 2022-09-13 15:20:10 +08:00 committed by GitHub
commit 4d34142d9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 206 additions and 190 deletions

View file

@ -12,32 +12,21 @@ struct FeedCardNavigationLink: View {
@ObservedObject var viewModel: HomeFeedViewModel
var body: some View {
let destination = LinkItemDetailView(
linkedItemObjectID: item.objectID,
isPDF: item.isPDF
)
#if os(iOS)
let modifiedDestination = destination
.navigationTitle("")
#else
let modifiedDestination = destination
#endif
return ZStack {
NavigationLink(
destination: modifiedDestination,
tag: item.objectID,
selection: $viewModel.selectedLinkItem
) {
EmptyView()
}
.opacity(0)
.buttonStyle(PlainButtonStyle())
.onAppear {
Task { await viewModel.itemAppeared(item: item, dataService: dataService, audioSession: audioSession) }
}
FeedCard(item: item) {
viewModel.selectedLinkItem = item.objectID
ZStack {
Button(
action: {
viewModel.selectedItem = item
viewModel.linkIsActive = true
}) {
NavigationLink(destination: EmptyView()) {
EmptyView()
}
.opacity(0)
.buttonStyle(PlainButtonStyle())
.onAppear {
Task { await viewModel.itemAppeared(item: item, dataService: dataService, audioSession: audioSession) }
}
FeedCard(item: item)
}
}
}
@ -60,28 +49,14 @@ struct GridCardNavigationLink: View {
scale = 0.95
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(150)) {
scale = 1.0
viewModel.selectedLinkItem = item.objectID
viewModel.selectedItem = item
viewModel.linkIsActive = true
}
}
var body: some View {
let destination = LinkItemDetailView(
linkedItemObjectID: item.objectID,
isPDF: item.isPDF
)
#if os(iOS)
let modifiedDestination = destination
.navigationTitle("")
#else
let modifiedDestination = destination
#endif
return ZStack {
NavigationLink(
destination: modifiedDestination,
tag: item.objectID,
selection: $viewModel.selectedLinkItem
) {
ZStack {
NavigationLink(destination: EmptyView()) {
EmptyView()
}
GridCard(item: item, isContextMenuOpen: $isContextMenuOpen, actionHandler: actionHandler, tapAction: {

View file

@ -111,11 +111,12 @@ import Views
guard let objectID = dataService.persist(jsonArticle: jsonArticle) else { return }
guard let linkedItem = dataService.viewContext.object(with: objectID) as? LinkedItem else { return }
viewModel.pushFeedItem(item: linkedItem)
viewModel.selectedLinkItem = linkedItem.objectID
viewModel.selectedItem = linkedItem
viewModel.linkIsActive = true
}
.onReceive(NSNotification.pushReaderItemPublisher) { notification in
if let objectID = notification.userInfo?["objectID"] as? NSManagedObjectID {
viewModel.handleReaderItemNotification(objectID: objectID)
viewModel.handleReaderItemNotification(objectID: objectID, dataService: dataService)
}
}
.onOpenURL { url in
@ -268,115 +269,123 @@ import Views
@ObservedObject var viewModel: HomeFeedViewModel
var body: some View {
List {
Section {
ForEach(viewModel.items) { item in
FeedCardNavigationLink(
item: item,
viewModel: viewModel
)
.contextMenu {
Button(
action: { viewModel.itemUnderTitleEdit = item },
label: { Label("Edit Title/Description", systemImage: "textbox") }
ZStack {
NavigationLink(
destination: LinkDestination(selectedItem: viewModel.selectedItem),
isActive: $viewModel.linkIsActive
) {
EmptyView()
}
List {
Section {
ForEach(viewModel.items) { item in
FeedCardNavigationLink(
item: item,
viewModel: viewModel
)
Button(
action: { viewModel.itemUnderLabelEdit = item },
label: { Label("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"
.contextMenu {
Button(
action: { viewModel.itemUnderTitleEdit = item },
label: { Label("Edit Title/Description", systemImage: "textbox") }
)
})
Button(
action: {
itemToRemove = item
confirmationShown = true
},
label: { Label("Delete", systemImage: "trash") }
)
if FeatureFlag.enableSnooze {
Button {
viewModel.itemToSnoozeID = item.id
viewModel.snoozePresented = true
} label: {
Label { Text("Snooze") } icon: { Image.moon }
}
}
Button(
action: { viewModel.downloadAudio(audioSession: audioSession, item: item) },
label: { Label("Download Audio", systemImage: "icloud.and.arrow.down") }
)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !item.isArchived {
Button {
Button(
action: { viewModel.itemUnderLabelEdit = item },
label: { Label("Edit Labels", systemImage: "tag") }
)
Button(action: {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: true)
viewModel.setLinkArchived(
dataService: dataService,
objectID: item.objectID,
archived: !item.isArchived
)
}
} label: {
Label("Archive", systemImage: "archivebox")
}.tint(.green)
} else {
Button {
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: .trailing, allowsFullSwipe: true) {
Button(
role: .destructive,
action: {
itemToRemove = item
confirmationShown = true
},
label: {
Image(systemName: "trash")
}
)
}.alert("Are you sure?", isPresented: $confirmationShown) {
Button("Remove Link", role: .destructive) {
if let itemToRemove = itemToRemove {
withAnimation {
viewModel.removeLink(dataService: dataService, objectID: itemToRemove.objectID)
}, label: {
Label(
item.isArchived ? "Unarchive" : "Archive",
systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox"
)
})
Button(
action: {
itemToRemove = item
confirmationShown = true
},
label: { Label("Delete", systemImage: "trash") }
)
if FeatureFlag.enableSnooze {
Button {
viewModel.itemToSnoozeID = item.id
viewModel.snoozePresented = true
} label: {
Label { Text("Snooze") } icon: { Image.moon }
}
}
self.itemToRemove = nil
Button(
action: { viewModel.downloadAudio(audioSession: audioSession, item: item) },
label: { Label("Download Audio", systemImage: "icloud.and.arrow.down") }
)
}
Button("Cancel", role: .cancel) { self.itemToRemove = nil }
}
.swipeActions(edge: .leading, allowsFullSwipe: true) {
if FeatureFlag.enableSnooze {
Button {
viewModel.itemToSnoozeID = item.id
viewModel.snoozePresented = true
} label: {
Label { Text("Snooze") } icon: { Image.moon }
}.tint(.appYellow48)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !item.isArchived {
Button {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: true)
}
} label: {
Label("Archive", systemImage: "archivebox")
}.tint(.green)
} else {
Button {
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: .trailing, allowsFullSwipe: true) {
Button(
role: .destructive,
action: {
itemToRemove = item
confirmationShown = true
},
label: {
Image(systemName: "trash")
}
)
}.alert("Are you sure?", isPresented: $confirmationShown) {
Button("Remove Link", role: .destructive) {
if let itemToRemove = itemToRemove {
withAnimation {
viewModel.removeLink(dataService: dataService, objectID: itemToRemove.objectID)
}
}
self.itemToRemove = nil
}
Button("Cancel", role: .cancel) { self.itemToRemove = nil }
}
.swipeActions(edge: .leading, allowsFullSwipe: true) {
if FeatureFlag.enableSnooze {
Button {
viewModel.itemToSnoozeID = item.id
viewModel.snoozePresented = true
} label: {
Label { Text("Snooze") } icon: { Image.moon }
}.tint(.appYellow48)
}
}
}
}
}
if viewModel.isLoading {
LoadingSection()
if viewModel.isLoading {
LoadingSection()
}
}
.listStyle(PlainListStyle())
}
.listStyle(PlainListStyle())
}
}
@ -411,47 +420,56 @@ import Views
}
var body: some View {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 325), spacing: 16)], spacing: 16) {
ForEach(viewModel.items) { item in
GridCardNavigationLink(
item: item,
actionHandler: { contextMenuActionHandler(item: item, action: $0) },
isContextMenuOpen: $isContextMenuOpen,
viewModel: viewModel
)
.alert("Are you sure?", isPresented: $confirmationShown) {
Button("Remove Link", role: .destructive) {
if let itemToRemove = itemToRemove {
withAnimation {
viewModel.removeLink(dataService: dataService, objectID: itemToRemove.objectID)
}
}
self.itemToRemove = nil
}
Button("Cancel", role: .cancel) { self.itemToRemove = nil }
}
ZStack {
ScrollView {
NavigationLink(
destination: LinkDestination(selectedItem: viewModel.selectedItem),
isActive: $viewModel.linkIsActive
) {
EmptyView()
}
}
.padding()
.background(
GeometryReader {
Color(.systemGroupedBackground).preference(
key: ScrollViewOffsetPreferenceKey.self,
value: $0.frame(in: .global).origin.y
)
}
)
.onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { offset in
DispatchQueue.main.async {
if !viewModel.isLoading, offset > 240 {
loadItems(isRefresh: true)
}
}
}
if viewModel.items.isEmpty, viewModel.isLoading {
LoadingSection()
LazyVGrid(columns: [GridItem(.adaptive(minimum: 325), spacing: 16)], spacing: 16) {
ForEach(viewModel.items) { item in
GridCardNavigationLink(
item: item,
actionHandler: { contextMenuActionHandler(item: item, action: $0) },
isContextMenuOpen: $isContextMenuOpen,
viewModel: viewModel
)
.alert("Are you sure?", isPresented: $confirmationShown) {
Button("Remove Link", role: .destructive) {
if let itemToRemove = itemToRemove {
withAnimation {
viewModel.removeLink(dataService: dataService, objectID: itemToRemove.objectID)
}
}
self.itemToRemove = nil
}
Button("Cancel", role: .cancel) { self.itemToRemove = nil }
}
}
}
.padding()
.background(
GeometryReader {
Color(.systemGroupedBackground).preference(
key: ScrollViewOffsetPreferenceKey.self,
value: $0.frame(in: .global).origin.y
)
}
)
.onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { offset in
DispatchQueue.main.async {
if !viewModel.isLoading, offset > 240 {
loadItems(isRefresh: true)
}
}
}
if viewModel.items.isEmpty, viewModel.isLoading {
LoadingSection()
}
}
}
}
@ -480,3 +498,27 @@ struct ScrollViewOffsetPreferenceKey: PreferenceKey {
}
}
#endif
struct LinkDestination: View {
let selectedItem: LinkedItem?
var body: some View {
Group {
if let selectedItem = selectedItem {
let destination = LinkItemDetailView(
linkedItemObjectID: selectedItem.objectID,
isPDF: selectedItem.isPDF
)
#if os(iOS)
let modifiedDestination = destination
.navigationTitle("")
#else
let modifiedDestination = destination
#endif
modifiedDestination
} else {
EmptyView()
}
}
}
}

View file

@ -22,10 +22,13 @@ import Views
@Published var negatedLabels = [LinkedItemLabel]()
@Published var snoozePresented = false
@Published var itemToSnoozeID: String?
@Published var selectedLinkItem: NSManagedObjectID?
@Published var linkRequest: LinkRequest?
@Published var showLoadingBar = false
@Published var appliedSort = LinkedItemSort.newest.rawValue
@Published var selectedItem: LinkedItem?
@Published var linkIsActive = false
@AppStorage(UserDefaultKey.audioInfoAlertShown.rawValue) var showAudioInfoAlert = false
@AppStorage(UserDefaultKey.lastSelectedLinkedItemFilter.rawValue) var appliedFilter = LinkedItemFilter.inbox.rawValue
@ -34,8 +37,9 @@ import Views
from: Date(timeIntervalSinceReferenceDate: 0)
)
func handleReaderItemNotification(objectID: NSManagedObjectID) {
selectedLinkItem = objectID
func handleReaderItemNotification(objectID: NSManagedObjectID, dataService: DataService) {
selectedItem = dataService.viewContext.object(with: objectID) as? LinkedItem
linkIsActive = true
}
var cursor: String?

View file

@ -3,12 +3,10 @@ import SwiftUI
import Utils
public struct FeedCard: View {
let tapHandler: () -> Void
@ObservedObject var item: LinkedItem
public init(item: LinkedItem, tapHandler: @escaping () -> Void) {
public init(item: LinkedItem) {
self.item = item
self.tapHandler = tapHandler
}
public var body: some View {
@ -77,9 +75,6 @@ public struct FeedCard: View {
}
}
.padding(.top, 8)
.onTapGesture {
tapHandler()
}
}
}
.padding(.top, 10)